Skip to content

superkabuki/threefive

Repository files navigation

Install |SCTE-35 Cli | SCTE-35 HLS | Cue Class | Stream Class | Online SCTE-35 Parser | Encode SCTE-35 | SCTE-35 Examples | SCTE-35 XML and More XML | SuperKabuki SCTE-35 MPEGTS Packet Injection | SCTE-35 As a Service

threefive is the most advanced SCTE-35 tool available*

* No asterisks, no exceptions.

Heads up

I called this repo threefive3 in an attempt to avoid confusion with my old threefive repo,
that didn't work at all.
 
 Going forward, this will be threefive and will be released as such.
 
 Adrian
 

v3.0.49 is the latest

Q. What is SCTE-35?

A. SCTE-35 is just encoded binary data. The SCTE-35 data is used to signal when to splice in commercials and such. The decoded variables in SCTE-35 are used determine the type and length of commercials. As with most things related to streaming video, it's way too complicated, but threefive makes it much easier.


  • Decode SCTE-35 in TEN formats: MPEGTS, Base64, Bytes, Dicts, Hex,HLS, Integers,JSON,XML and XML+Binary.
  • Encode SCTE-35 in EIGHT formats: MPEGTS, Base64, Bytes, Hex, Int, JSON,XML, and Xml+binary.
  • All HLS SCTE-35 Tags are Supported.
  • Automatic AES decryption for HLS.
  • Built-in Multicast Sender and Receiver
  • The SuperKabuki SCTE-35 MPEGTS Packet Injection Engine was added to threefive v3.0.39

  • If you just want to decode SCTE-35 data into JSON, try Sassy .
  • If you just want to encode JSON into SCTE-35, Try Sassy .
  • If you just want to decode and encode SCTE-35 in your browser, Try Sassy.
  • If you can make an https request, you can decode SCTE-35 with Sassy , no installation required.
  • powered by nstuff

MPEGTS streams can be parsed for SCTE-35 with three lines of code.

a@fu:~/build5/scte35/scte35$ pypy3

>>>> from threefive import Stream
>>>> strm=Stream('https://futzu.com/xaa.ts')
>>>> strm.decode()

Checkout the Super Cool SCTE-35 Examples




The new xml stuff is really sweet, you can even pass an DASH MPD directly to a Cue instance, and it will parse the first Event with SCTE-35 it finds. Let me show you.

a@fu:~/threefive$ pypy3

Python 3.9.16 (7.3.11+dfsg-2+deb12u3, Dec 30 2024, 22:36:23)
[PyPy 7.3.11 with GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>> 
>>>> from threefive import Cue,reader
>>>> data = reader('https://demo.unified-streaming.com/k8s/live/stable/scte35-n\
o-splicing.isml/.mpd').read()    # use reader to pull the mpd over a network.
>>>> cue=Cue(data)     # pass in to a Cue instance
>>>> cue.show()
{
    "info_section": {
        "table_id": "0xfc",
        "section_syntax_indicator": false,
        "private": false,
        "sap_type": "0x03",
        "sap_details": "No Sap Type",
        "section_length": 32,
        "protocol_version": 0,
        "encrypted_packet": false,
        "encryption_algorithm": 0,
        "pts_adjustment": 0.0,
        "cw_index": "0x00",
        "tier": "0x0fff",
        "splice_command_length": 15,
        "splice_command_type": 5,
        "descriptor_loop_length": 0,
        "crc": "0xe4612424"
    },
    "command": {
        "command_length": 15,
        "command_type": 5,
        "name": "Splice Insert",
        "break_auto_return": true,
        "break_duration": 38.4,
        "splice_event_id": 14432855,
        "splice_event_cancel_indicator": false,
        "out_of_network_indicator": true,
        "program_splice_flag": true,
        "duration_flag": true,
        "splice_immediate_flag": true,
        "event_id_compliance_flag": true,
        "unique_program_id": 49152,
        "avail_num": 0,
        "avails_expected": 0
    },
    "descriptors": []
}

Latest release is v3.0.49

  • cyclomatic complexity 2.01 ,That's a kickass score.

  • pylint score is 9.62/10, this needs a liitle work.

Stay up to date, only the latest release is supported.

  • Xml is back and better than ever. The new Ultra Xml Parser Supreme replaces the Super Xml Parser.

Documentation

Install

Examples

XML

  • XML New! updated 05/01/2025

Cli

  • SCTE-35 Cli Super Tool Encodes, Decodes, and Recodes. This is pretty cool, it does SCTE-35 seven different ways.
    • The cli tool comes with builtin documentation just type threefive help

HLS

MPEGTS Packet Injection

SCTE-35 As a Service

  • Decode SCTE-35 without installing anything. If you can make an https request, you can use Sassy to decode SCTE-35. .

Classes

  • The python built in help is always the most up to date docs for the library.
a@fu:~/build7/threefive$ pypy3

>>>> from threefive import Stream
>>>> help(Stream)

| more


Install

  • python3 via pip
python3 -mpip install threefive
  • pypy3
pypy3 -mpip install threefive
  • from the git repo
git clone https://github.com/superkabuki/scte35.git
cd threefive
make install

Using the library

  • Let me show you how easy threefive is to use.
  • well start off reading SCTE-35 xml from a file
a@fu:~/threefive$ pypy3
Python 3.9.16 (7.3.11+dfsg-2+deb12u3, Dec 30 2024, 22:36:23)
[PyPy 7.3.11 with GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>> from threefive import reader
>>>> from threefive import Cue
>>>> data =reader('/home/a/xml.xml').read()
* load it into a threefive.Cue instance
```py3
>>>> cue = Cue(data)
  • Show the data as JSON
>>>> cue.show()
{
    "info_section": {
        "table_id": "0xfc",
        "section_syntax_indicator": false,
        "private": false,
        "sap_type": "0x03",
        "sap_details": "No Sap Type",
        "section_length": 92,
        "protocol_version": 0,
        "encrypted_packet": false,
        "encryption_algorithm": 0,
        "pts_adjustment": 0.0,
        "cw_index": "0x00",
        "tier": "0x0fff",
        "splice_command_length": 15,
        "splice_command_type": 5,
        "descriptor_loop_length": 60,
        "crc": "0x7632935"
    },
    "command": {
        "command_length": 15,
        "command_type": 5,
        "name": "Splice Insert",
        "break_auto_return": false,
        "break_duration": 180.0,
        "splice_event_id": 1073743095,
        "splice_event_cancel_indicator": false,
        "out_of_network_indicator": true,
        "program_splice_flag": false,
        "duration_flag": true,
        "splice_immediate_flag": false,
        "event_id_compliance_flag": true,
        "unique_program_id": 1,
        "avail_num": 12,
        "avails_expected": 5
    },
    "descriptors": [
        {
            "tag": 0,
            "identifier": "CUEI",
            "name": "Avail Descriptor",
            "provider_avail_id": 12,
            "descriptor_length": 8
        },
        {
            "tag": 0,
            "identifier": "CUEI",
            "name": "Avail Descriptor",
            "provider_avail_id": 13,
            "descriptor_length": 8
        },
        {
            "tag": 0,
            "identifier": "CUEI",
            "name": "Avail Descriptor",
            "provider_avail_id": 14,
            "descriptor_length": 8
        },
        {
            "tag": 0,
            "identifier": "CUEI",
            "name": "Avail Descriptor",
            "provider_avail_id": 15,
            "descriptor_length": 8
        },
        {
            "tag": 0,
            "identifier": "CUEI",
            "name": "Avail Descriptor",
            "provider_avail_id": 16,
            "descriptor_length": 8
        },
        {
            "tag": 0,
            "identifier": "CUEI",
            "name": "Avail Descriptor",
            "provider_avail_id": 17,
            "descriptor_length": 8
        }
    ]
}
  • convert the data back to xml
>>>> print(cue.xml())
<scte35:SpliceInfoSection xmlns:scte35="https://scte.org/schemas/35"  ptsAdjustment="0" protocolVersion="0" sapType="3" tier="4095">
   <scte35:SpliceInsert spliceEventId="1073743095" spliceEventCancelIndicator="false" spliceImmediateFlag="false" eventIdComplianceFlag="true" availNum="12" availsExpected="5" outOfNetworkIndicator="true" uniqueProgramId="1">
      <scte35:BreakDuration autoReturn="false" duration="16200000"/>
   </scte35:SpliceInsert>
   <scte35:AvailDescriptor providerAvailId="12"/>
   <scte35:AvailDescriptor providerAvailId="13"/>
   <scte35:AvailDescriptor providerAvailId="14"/>
   <scte35:AvailDescriptor providerAvailId="15"/>
   <scte35:AvailDescriptor providerAvailId="16"/>
   <scte35:AvailDescriptor providerAvailId="17"/>
</scte35:SpliceInfoSection>
  • convert to xml+binary
>>>> print(cue.xmlbin())
<scte35:Signal xmlns:scte35="https://scte.org/schemas/35">
    <scte35:Binary>/DBcAAAAAAAAAP/wDwVAAAT3f69+APcxQAABDAUAPAAIQ1VFSQAAAAwACENVRUkAAAANAAhDVUVJAAAADgAIQ1VFSQAAAA8ACENVRUkAAAAQAAhDVUVJAAAAEQdjKTU=</scte35:Binary>
</scte35:Signal>
  • convert to base64
>>>> print(cue.base64())
/DBcAAAAAAAAAP/wDwVAAAT3f69+APcxQAABDAUAPAAIQ1VFSQAAAAwACENVRUkAAAANAAhDVUVJAAAADgAIQ1VFSQAAAA8ACENVRUkAAAAQAAhDVUVJAAAAEQdjKTU=
  • convert to hex
>>>> print(cue.hex())
0xfc305c00000000000000fff00f05400004f77faf7e00f7314000010c05003c0008435545490000000c0008435545490000000d0008435545490000000e0008435545490000000f000843554549000000100008435545490000001107632935
  • show just the splice command
>>>> cue.command.show()
{
    "command_length": 15,
    "command_type": 5,
    "name": "Splice Insert",
    "break_auto_return": false,
    "break_duration": 180.0,
    "splice_event_id": 1073743095,
    "splice_event_cancel_indicator": false,
    "out_of_network_indicator": true,
    "program_splice_flag": false,
    "duration_flag": true,
    "splice_immediate_flag": false,
    "event_id_compliance_flag": true,
    "unique_program_id": 1,
    "avail_num": 12,
    "avails_expected": 5
}
  • edit the break duration
>>>> cue.command.break_duration=30
>>>> cue.command.show()
{
    "command_length": 15,
    "command_type": 5,
    "name": "Splice Insert",
    "break_auto_return": false,
    "break_duration": 30,
    "splice_event_id": 1073743095,
    "splice_event_cancel_indicator": false,
    "out_of_network_indicator": true,
    "program_splice_flag": false,
    "duration_flag": true,
    "splice_immediate_flag": false,
    "event_id_compliance_flag": true,
    "unique_program_id": 1,
    "avail_num": 12,
    "avails_expected": 5
}
  • re-encode to base64 with the new duration
>>>> cue.base64()
'/DBcAAAAAAAAAP/wDwVAAAT3f69+ACky4AABDAUAPAAIQ1VFSQAAAAwACENVRUkAAAANAAhDVUVJAAAADgAIQ1VFSQAAAA8ACENVRUkAAAAQAAhDVUVJAAAAEe1FB6g='
  • re-encode to xml with the new duration
>>>> print(cue.xml())
<scte35:SpliceInfoSection xmlns:scte35="https://scte.org/schemas/35"  ptsAdjustment="0" protocolVersion="0" sapType="3" tier="4095">
   <scte35:SpliceInsert spliceEventId="1073743095" spliceEventCancelIndicator="false" spliceImmediateFlag="false" eventIdComplianceFlag="true" availNum="12" availsExpected="5" outOfNetworkIndicator="true" uniqueProgramId="1">
      <scte35:BreakDuration autoReturn="false" duration="2700000"/>
   </scte35:SpliceInsert>
   <scte35:AvailDescriptor providerAvailId="12"/>
   <scte35:AvailDescriptor providerAvailId="13"/>
   <scte35:AvailDescriptor providerAvailId="14"/>
   <scte35:AvailDescriptor providerAvailId="15"/>
   <scte35:AvailDescriptor providerAvailId="16"/>
   <scte35:AvailDescriptor providerAvailId="17"/>
</scte35:SpliceInfoSection>
  • show just the descriptors
>>>> _ = [d.show() for d in cue.descriptors]
{
    "tag": 0,
    "identifier": "CUEI",
    "name": "Avail Descriptor",
    "provider_avail_id": 12,
    "descriptor_length": 8
}
{
    "tag": 0,
    "identifier": "CUEI",
    "name": "Avail Descriptor",
    "provider_avail_id": 13,
    "descriptor_length": 8
}
{
    "tag": 0,
    "identifier": "CUEI",
    "name": "Avail Descriptor",
    "provider_avail_id": 14,
    "descriptor_length": 8
}
{
    "tag": 0,
    "identifier": "CUEI",
    "name": "Avail Descriptor",
    "provider_avail_id": 15,
    "descriptor_length": 8
}
{
    "tag": 0,
    "identifier": "CUEI",
    "name": "Avail Descriptor",
    "provider_avail_id": 16,
    "descriptor_length": 8
}
{
    "tag": 0,
    "identifier": "CUEI",
    "name": "Avail Descriptor",
    "provider_avail_id": 17,
    "descriptor_length": 8
}
  • pop off the last descriptor and re-encode to xml
>>>> cue.descriptors.pop()
{'tag': 0, 'identifier': 'CUEI', 'name': 'Avail Descriptor', 'private_data': None, 'provider_avail_id': 17, 'descriptor_length': 8}
>>>> print(cue.xml())
<scte35:SpliceInfoSection xmlns:scte35="https://scte.org/schemas/35"  ptsAdjustment="0" protocolVersion="0" sapType="3" tier="4095">
   <scte35:SpliceInsert spliceEventId="1073743095" spliceEventCancelIndicator="false" spliceImmediateFlag="false" eventIdComplianceFlag="true" availNum="12" availsExpected="5" outOfNetworkIndicator="true" uniqueProgramId="1">
      <scte35:BreakDuration autoReturn="false" duration="2700000"/>
   </scte35:SpliceInsert>
   <scte35:AvailDescriptor providerAvailId="12"/>
   <scte35:AvailDescriptor providerAvailId="13"/>
   <scte35:AvailDescriptor providerAvailId="14"/>
   <scte35:AvailDescriptor providerAvailId="15"/>
   <scte35:AvailDescriptor providerAvailId="16"/>
</scte35:SpliceInfoSection>

The Cli tool

The cli tool installs automatically with pip or the Makefile.

Inputs

  • Most inputs are auto-detected.
  • stdin is auto selected and auto detected.
  • SCTE-35 data is printed to stderr
  • stdout is used when piping video
  • mpegts can be specified by file name or URI.
threefive udp://@235.2.5.35:3535
  • If a file comtains a SCTE-35 cue as a string( base64,hex,int,json,or xml+bin), redirect the file contents.
  threefive < json.json  
  • quoted strings(( base64,hex,int,json or xml+bin), can be passed directly on the command line as well.
threefive '/DAWAAAAAAAAAP/wBQb+ztd7owAAdIbbmw=='
Input Type Cli Example
Base64 threefive '/DAsAAAAAyiYAP/wCgUAAAABf1+ZmQEBABECD0NVRUkAAAAAf4ABADUAAC2XQZU='
Hex threefive 0xfc301600000000000000fff00506fed605225b0000b0b65f3b
HLS threefive hls https://example.com/master.m3u8
JSON threefive < json.json
Xmlbin js threefive < xmlbin.xml

Streams

Protocol Cli Example
File threefive video.ts
Http(s) threefive https://example.com/video.ts
Stdin threefive < video.ts
UDP Multicast threefive udp://@235.35.3.5:9999
UDP Unicast threefive udp://10.0.0.7:5555
HLS threefive hls https://example.com/master.m3u8

Outputs

  • output type is determined by the key words base64, bytes, hex, int, json, and xmlbin.
  • json is the default.
  • Any input (except HLS,) can be returned as any output
    • examples Base64 to Hex etc...)
Output Type Cli Example
Base 64 threefive 0xfc301600000000000000fff00506fed605225b0000b0b65f3b base64
Bytes threefive 0xfc301600000000000000fff00506fed605225b0000b0b65f3b bytes
Hex threefive '/DAsAAAAAyiYAP/wCgUAAAABf1+ZmQEBABECD0NVRUkAAAAAf4ABADUAAC2XQZU=' hex
Integer threefive '/DAsAAAAAyiYAP/wCgUAAAABf1+ZmQEBABECD0NVRUkAAAAAf4ABADUAAC2XQZU=' int
JSON threefive 0xfc301600000000000000fff00506fed605225b0000b0b65f3b json
Xml+bin threefive 0xfc301600000000000000fff00506fed605225b0000b0b65f3b xmlbin

hls

  • parse hls manifests and segments for SCTE-35
threefive hls https://example.com/master.m3u8

Iframes

  • Show iframes PTS in an MPEGTS video
threefive iframes https://example.com/video.ts

packets

  • Print raw SCTE-35 packets from multicast mpegts video
threefive packets udp://@235.35.3.5:3535

proxy

  • Parse a https stream and write raw video to stdout
threefive proxy video.ts

pts

  • Print PTS from mpegts video
threefive pts video.ts

sidecar

  • Parse a stream, write pts,write SCTE-35 Cues to sidecar.txt
threefive sidecar video.ts

sixfix

  • Fix SCTE-35 data mangled by ffmpeg
threefive sixfix video.ts

show

  • Probe mpegts video ( kind of like ffprobe )
 threefive show video.ts

version

  • Show version
 threefive version

help

  • Help
 threefive help

Stream Multicast with the threefive cli, it's easy.

  • The threefive cli has long been a Multicast Receiver( client )
  • The cli now comes with a builtin Multicast Sender( server).
  • It's optimized for MPEGTS (1316 byte Datagrams) but you can send any video or file.
  • The defaults will work in most situations, you don't even have to set the address.
  • threefive cli also supports UDP Unicast Streaming.

image

a@fu:~$ threefive mcast help
usage: threefive mcast [-h] [-i INPUT] [-a ADDR] [-b BIND_ADDR] [-t TTL]

optional arguments:
  -h, --help            show this help message and exit
  -i INPUT, --input INPUT
                        like "/home/a/vid.ts" or "udp://@235.35.3.5:3535" or
                        "https://futzu.com/xaa.ts"
                        [default:sys.stdin.buffer]
  -a ADDR, --addr ADDR  Destination IP:Port [default:235.35.3.5:3535]
  -b BIND_ADDR, --bind_addr BIND_ADDR
                        Local IP to bind [default:0.0.0.0]
  -t TTL, --ttl TTL     Multicast TTL (1 - 255) [default:32]
a@fu:~$ 

Install |SCTE-35 Cli | SCTE-35 HLS | Cue Class | Stream Class | Online SCTE-35 Parser | Encode SCTE-35 | SCTE-35 Examples | SCTE-35 XML and More XML | threefive runs Four Times Faster on pypy3 | SuperKabuki SCTE-35 MPEGTS Packet Injection