Skip to main content

threefive is The #1 SCTE-35 Decoder and Encoder on the Planet

Project description

[ threefive ]

https://github.com/superkabuki/threefive

threefive is the industry leading SCTE-35 tool.


[ Terms and Conditions ]

  • You be cool, and I'll be cool.
  • Questions? Ask them.
  • Features? Speak up. I'm open to new ideas..
  • Comments? Make them.
  • Got Code? Do a pull request.

[ News ]


[ Latest version is v3.0.71 ]

  • why can`t you be more cyclomatic like your brother?
  • All ansi color codes striped when output is not a tty.
  • threefive.Cue.xmlbin() now returns a threefive.xml.Node instance like threefive.Cue.xml().
  • threefive.Stream has a new method threefive.Stream.rt(func=show_cue) that parses SCTE-35 and proxies the stream to stdout at realtime speed.
  • threefive.Stream.decode() now shows parse speed.
  • Cli tool new features
    • __New bump keyword to adjust SCTE-35 PTS. Supports positive and negative adjustments.
    • New rt keyword parses SCTE-35, writes a SCTE-35 sidecar file, and outputs the MPEGTS stream to stdout at realtime speed.
    • All MPEGTS related tasks show parsing speed.
    • threefive mcast now throttles Multicast output to realtime speed.
    • threefive packets and threefive pts now works with SRT.

[ Features ]

  • Decode SCTE-35 from MPEGTS ✔ Base64 ✔ Bytes ✔ DASH ✔ Hex ✔ HLS ✔ Integers ✔ JSON ✔ XML ✔ XML+Binary ✔ DASH ✔ Dicts ✔

  • Encodes SCTE-35 to MPEGTS ✔ Base64 ✔ Bytes ✔ Hex ✔ Integers ✔ JSON ✔ XML ✔ XML+Binary ✔

  • threefive file protocols Pipes(stdin, stdout) ✔ Files ✔

  • threefive network protocols HTTP(s) ✔ Multicast ✔ UDP ✔ SRT ✔

  • Automatic AES decryption for MPEGTS and HLS. ✔

  • Built-in Multicast Sender and Receiver. ✔

  • Injects SCTE-35 Packets into MPEGTS video ✔.


[ Tip Of The Week ]

  • Stream.decode_next() is now a generator
	from threefive import Stream

    strm =Stream(some_url)
    for cue in strm.decode_next():
		your_complicated_function(cue, one_arg , two_arg, 13,)

[ Documentation ]

[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

[Quick Start]

  • Most of the stuff in threefive all works the same way.

[cli tool]

  • The default action is to read a input and write a SCTE-35 output.

    • Inputs: mpegts, base64, hex, json,and xml, and xmlbin.

    • Outputs: base64, bytes, hex, int, json, xml, and xmlbin.

    • Sources: SCTE35 can read from strings, files, stdin, http(s), multicast,srt and udp.

Input Output How to use
mpegts base64 threefive https://example.com/video.ts base64
base64 hex threefive '/DAWAAAAAAAAAP/wBQb+AKmKxwAACzuu2Q==' hex
xmlbin int threefive < xmlbin.xml int
xml json threefive < xml.xml
mpegts xml+bin threefive video.ts xmlbin
json xml threefive < json.json xml
  • Additional functionality in the threefive cli tool.
Description How To Use
Adjust SCTE-35 PTS values by seconds threefive bump -i input.ts -o output.ts -b -37.45
Parse HLS for SCTE35 threefive hls https://example.com/master.m3u8
Inject SCTE35 packets threefive inject -i in.video -s sidecar.txt -o out.ts
Show raw SCTE35 packets threefive packets udp://@235.35.3.5:3535
Copy MPEGTS stream to stdout at realtime speed threefive rt input.ts
Create SCTE35 sidecar file threefive sidecar video.ts
Fix SCTE-35 data mangled by ffmpeg threefive sixfix video.ts
Show streams in mpegts stream threefive show https://example.com/video.ts
Show iframes in mpegts stream threefive iframes srt://10.10.1.3:9000
Show PTS values from mpegts stream threefive pts udp://192.168.1.10:9000
Proxy the mpegts stream to stdout threefive proxy https://wexample.com/video.ts
Multicast anything threefive mcast some.file

[Examples]

mpegts

  • MPEGTS streams can be Files, Http(s), Multicast,SRT, UDP Unicast, or stdin.

  • cli

threefive https://example.com/video.ts
  • wildcards work too.
threefive /mpegts/*.ts
  • lib
from threefive import Stream
stream = Stream('https://example.com/video.ts')
stream.decode()

Base64

  • cli
threefive '/DAsAAAAAyiYAP/wCgUAAAABf1+ZmQEBABECD0NVRUkAAAAAf4ABADUAAC2XQZU='
  • lib
from threefive import Cue
data = '/DAsAAAAAyiYAP/wCgUAAAABf1+ZmQEBABECD0NVRUkAAAAAf4ABADUAAC2XQZU='
cue=Cue(data)
cue.show()

Bytes

  • cli

    • Bytes don't work on the cli
  • lib

from threefive import Cue
data =  b'\xfc0\x16\x00\x00\x00\x00\x00\x00\x00\xff\xf0\x05\x06\xfe\x00\xc0D\xa0\x00\x00\x00\xb5k\x88'
cue=Cue(data)
cue.show()

Hex

  • Can be a hex literal or hex string or bytes.

  • cli

threefive  0xfc301600000000000000fff00506fed605225b0000b0b65f3b
  • lib
from threefive import Cue
data =  0xfc301600000000000000fff00506fed605225b0000b0b65f3b
cue=Cue(data)
cue.show()

Int

  • Can be a literal integer or string or bytes.

  • cli

threefive  1583008701074197245727019716796221243043855984942057168199483
  • lib
from threefive import Cue
data =  1583008701074197245727019716796221243043855984942057168199483
cue=Cue(data)
cue.show()

JSON

  • cli
    • put JSON SCTE-35 in a file and redirect it into threefive
threefive  < json.json
  • lib
 from threefive import Cue
 data = '''{
    "info_section": {
        "table_id": "0xfc",
        "section_syntax_indicator": false,
        "private": false,
        "sap_type": "0x03",
        "sap_details": "No Sap Type",
        "section_length": 22,
        "protocol_version": 0,
        "encrypted_packet": false,
        "encryption_algorithm": 0,
        "pts_adjustment": 0.0,
        "cw_index": "0x00",
        "tier": "0x0fff",
        "splice_command_length": 5,
        "splice_command_type": 6,
        "descriptor_loop_length": 0,
        "crc": "0xb56b88"
    },
    "command": {
        "command_length": 5,
        "command_type": 6,
        "name": "Time Signal",
        "time_specified_flag": true,
        "pts_time": 140.005333
    },
    "descriptors": []
}
'''
cue=Cue(data)
cue.show()

Xml

  • cli
    • put xml SCTE-35 in a file and redirect it into threefive
     threefive < xml.xml
    
  • lib
from threefive import Cue
data =  '''
<scte35:SpliceInfoSection xmlns:scte35="https://scte.org/schemas/35" 
        ptsAdjustment="0" protocolVersion="0" sapType="3" tier="4095">
   <scte35:TimeSignal>
      <scte35:SpliceTime ptsTime="12600480"/>
   </scte35:TimeSignal>
</scte35:SpliceInfoSection>
'''
cue=Cue(data)

cue.show()

Xml+binary

  • cli
    • write xml+binary to a file and redirect it to threefive
threefive < xmlbin.xml
  • lib
from threefive import Cue
data = '''<scte35:Signal xmlns:scte35="https://scte.org/schemas/35">
    <scte35:Binary>/DAWAAAAAAAAAP/wBQb+AMBEoAAAALVriA==</scte35:Binary>
</scte35:Signal>
'''
cue=Cue(data)
cue.show()

More 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)

[threefive now supports SRT]

  • ( You have to unmute the audio )

https://github.com/user-attachments/assets/a323ea90-867f-480f-a55f-e9339263e511



[more]


Python3 vs Pypy3 running threefive

  • ( You have to unmute the audio )

https://github.com/user-attachments/assets/9e88fb38-6ad0-487a-a801-90faba9d72c6


Using the library

  • Let me show you how easy threefive is to use.

  • 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
>>>> 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
        },
      

    ]
}
  • 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

[ threefive Streams Multicast, it's easy. ]

  • The threefive cli has long been a Multicast Receiver( client )
  • The cli now comes with a builtin Multicast Sender( server).
  • Start the Receiver first
  • 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.
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:~$ 
  • the video shows three streams being read and played from threefive's multicast, one stream is being converted to srt.
  • the command
a@fu:~/scratch/threefive$ threefive mcast -i ~/mpegts/ms.ts 

https://github.com/user-attachments/assets/df95b8da-5ca6-4bf3-b029-c95204841e43


iodisco.com/scte35

image

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

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

threefive-3.0.73.tar.gz (90.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

threefive-3.0.73-py3-none-any.whl (85.5 kB view details)

Uploaded Python 3

File details

Details for the file threefive-3.0.73.tar.gz.

File metadata

  • Download URL: threefive-3.0.73.tar.gz
  • Upload date:
  • Size: 90.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.2

File hashes

Hashes for threefive-3.0.73.tar.gz
Algorithm Hash digest
SHA256 70ad80f352fa5a02247b1fdad87c672e9ad73edd66ad00663598145d4b2efaab
MD5 d08b80e2e641e232cf4b9df4cf42efc5
BLAKE2b-256 e1a93dfb3826ed8d577450abcc33a3b59e133d022de6111fdd5401cbe2e78fa8

See more details on using hashes here.

File details

Details for the file threefive-3.0.73-py3-none-any.whl.

File metadata

  • Download URL: threefive-3.0.73-py3-none-any.whl
  • Upload date:
  • Size: 85.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.2

File hashes

Hashes for threefive-3.0.73-py3-none-any.whl
Algorithm Hash digest
SHA256 53ddc4e8715936f6dbd0d14a45b87e554536ba158659da47a38f30f05bb012b5
MD5 1b3688486c660a3baedc2d51748eeb5a
BLAKE2b-256 bacb3cb307d690e5a448fcabea3928dc6fde4fca6dcd92c64ac84d2e681445b3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page