threefive is the highest rated SCTE35 parser, ever, probably, maybe. Super cool SCTE35 decoder and SCTE35 encoder.

futzu futzu Last update: Aug 27, 2022

Install | Fast Start |Cue Class | Stream Class |Examples |ffmpeg and threefive

threefive is a SCTE35 parser.

  • All 2020 SCTE35
    • Commands
    • Descriptors
    • Upids
  • Streaming Network Protocols
    • Multicast
    • UDP
    • Http(s)

Using a library means you shouldn't have to write a lot of code.

#!/usr/bin/env python3"""35.py    parses a stream for SCTE-35,    prints SCTE-35 messages"""import sysimport threefiveif __name__ == "__main__":    arg = sys.argv[1]    strm = threefive.Stream(arg)    strm.decode()

Requirements

  • threefive requires
  • optional dependencies:
    • pyaes If you want AES decryption for HLS segments.

Install

python3 -mpip  install  threefive# and / orpypy3 -m pip install threefive
  • To install the optional dependencies.
python3 -mpip  install threefive[all]# and / orpypy3 -mpip  install  threefive[all]

Versions and Releases

Python 3.10.6 (main, Aug 10 2022, 11:19:32) [GCC 12.1.0] on linuxType "help", "copyright", "credits" or "license" for more information.>>> from threefive import version>>> version()'2.3.45'>>> 
  • I do a lot of releases.

  • Anytime I fix a bug or add a feature, I bump the version and make a pip package.

  • For best results, stay up with me.

  • Release versions are odd.

  • Unstable testing versions are even.

  • The Lastest Release is the Only Supported Version

    • If you ask for help, I'm going to ask you to upgrade to the latest version.

threefive.version() returns the version as a string.


Easy Examples

Base64
>>> from threefive import Cue>>> stuff = '/DAvAAAAAAAA///wBQb+dGKQoAAZAhdDVUVJSAAAjn+fCAgAAAAALKChijUCAKnMZ1g='>>> cue=Cue(stuff)>>> cue.decode()True
Bytes
>>> import threefive >>> stuff = b'\xfc0\x11\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\x00\x00\x00O%3\x96'>>> cue=Cue(stuff)>>> cue.decode()True>>> cue.show()
Hex
import threefive cue = threefive.Cue("0XFC301100000000000000FFFFFF0000004F253396")cue.decode()cue.show()
Mpegts Multicast
  • On my Debian Sid laptop I set the following,
## <dev> is the network deviceip link set <dev> multicast on allmulticast onethtool  -G <dev> rx 4096sysctl -w net.core.rmem_default=5000000sysctl -w net.core.rmem_max=15000000
import threefive strm = threefive.Stream('udp://@239.35.0.35:1234')strm.decode()

Cue Class

  • src cue.py
  • The threefive.Cue class decodes a SCTE35 binary, base64, or hex encoded string.
    >>>> import threefive    >>>> Base64 = "/DAvAAAAAAAA///wBQb+dGKQoAAZAhdDVUVJSAAAjn+fCAgAAAAALKChijUCAKnMZ1g="    >>>> cue = threefive.Cue(Base64)
  • cue.decode() returns True on success,or False if decoding failed
    >>>> cue.decode()    True
  • After Calling cue.decode() the instance variables can be accessed via dot notation.
    >>>> cue.command    {'calculated_length': 5, 'name': 'Time Signal', 'time_specified_flag': True, 'pts_time': 21695.740089}    >>>> cue.command.pts_time    21695.740089    >>>> cue.info_section.table_id    '0xfc'
  • When parsing Cues from MPEGTS, threefive tries to include,
    • pid of the packet
    • program of the pid
    • pts of the packet
    • pcr of the packet
class Cue(threefive.base.SCTE35Base) |  Cue(data=None, packet_data=None) 
 |  __init__(self, data=None, packet_data=None) |      data may be packet bites or encoded string |      packet_data is a instance passed from a Stream instance

Cue.decode()

 |  decode(self) |      Cue.decode() parses for SCTE35 data

Cue.get()

 |  get(self) |      Cue.get returns the SCTE-35 Cue |      data as a dict of dicts.

Cue.get() Example

>>> from threefive import Cue>>> cue = Cue('0XFC301100000000000000FFFFFF0000004F253396')>>> cue.decode()True>>> cue{'bites': b'\xfc0\x11\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\x00\x00\x00O%3\x96', 'info_section': {'table_id': '0xfc', 'section_syntax_indicator': False, 'private': False, 'sap_type': '0x3', 'sap_details': 'No Sap Type', 'section_length': 17, 'protocol_version': 0, 'encrypted_packet': False, 'encryption_algorithm': 0, 'pts_adjustment_ticks': 0, 'pts_adjustment': 0.0, 'cw_index': '0x0', 'tier': '0xfff','splice_command_length': 4095, 'splice_command_type': 0, 'descriptor_loop_length': 0, 'crc': '0x4f253396'},'command': {'command_length': None, 'command_type': 0, 'name': 'Splice Null'},'descriptors': [], 'packet_data': None}
  • Cue.get() omits cue.bites and empty values
>>> cue.get(){'info_section': {'table_id': '0xfc', 'section_syntax_indicator': False,'private': False, 'sap_type': '0x3', 'sap_details': 'No Sap Type', 'section_length': 17, 'protocol_version': 0, 'encrypted_packet': False,'encryption_algorithm': 0, 'pts_adjustment_ticks': 0, 'pts_adjustment': 0.0, 'cw_index': '0x0', 'tier': '0xfff','splice_command_length': 4095, 'splice_command_type': 0, 'descriptor_loop_length': 0, 'crc': '0x4f253396'},'command': {'command_type': 0, 'name': 'Splice Null'},'descriptors': []}

Cue.get_descriptors()

 |  get_descriptors(self) |      Cue.get_descriptors returns a list of |      SCTE 35 splice descriptors as dicts.

Cue.get_json()

 |  get_json(self) |      Cue.get_json returns the Cue instance |      data in json.

Cue.show()

 |  show(self) |      Cue.show prints the Cue as JSON

Cue.to_stderr()

 |  to_stderr(self) |      Cue.to_stderr prints the Cue

Stream Class

  • src stream.py
  • The threefive.Stream class parses SCTE35 from Mpegts.
  • Supports:
    • File and Http(s) and Udp and Multicast protocols.
    • Multiple Programs.
    • Multi-Packet PAT, PMT, and SCTE35 tables.
class Stream(builtins.object) |  Stream(tsdata, show_null=True) |   |  Stream class for parsing MPEG-TS data.
|  __init__(self, tsdata, show_null=True)|      |      tsdata is a file or http, https, |       udp or multicast url.|       |      set show_null=False to exclude Splice Nulls|      |      Use like...|      |      from threefive import Stream|      strm = Stream("vid.ts",show_null=False)|      strm.decode()

Stream.decode(func=show_cue)

|  decode(self, func=show_cue)|      Stream.decode reads self.tsdata to find SCTE35 packets.|      func can be set to a custom function that accepts|      a threefive.Cue instance as it's only argument.

Stream.decode Example

import sysfrom threefive import Stream>>>> Stream('plp0.ts').decode()
  • Pass in custom function

  • func should match the interface func(cue)

Stream.decode with custom function Example

import sysimport threefivedef display(cue):   print(f'\033[92m{cue.packet_data}\033[00m')   print(f'{cue.command.name}')def do():   sp = threefive.Stream(tsdata)   sp.decode(func = display)       if __name__ == '__main__':    do()

Stream.decode_next()

|  decode_next(self)|      Stream.decode_next returns the next|      SCTE35 cue as a threefive.Cue instance.

Stream.decode_next Example

import sysimport threefivedef do():    arg = sys.argv[1]    with open(arg,'rb') as tsdata:        st = threefive.Stream(tsdata)        while True:            cue = st.decode_next()            if not cue:                return False            if cue:                cue.show()if __name__ == "__main__":    do()

Stream.decode_program(the_program, func = show_cue)

|  decode_program(self, the_program, func=show_cue)|      Stream.decode_program limits SCTE35 parsing|      to a specific MPEGTS program.

Stream.decode_program Example

import threefivethreefive.Stream('35.ts').decode_program(1)

Stream.decode_proxy(func = show_cue)

  • Writes all packets to sys.stdout.

  • Writes scte35 data to sys.stderr.

|  decode_proxy(self, func=show_cue_stderr)|      Stream.decode_proxy writes all ts packets are written to stdout|      for piping into another program like mplayer.|      SCTE-35 cues are printed to stderr.

Stream.decode_proxy Example

import threefivesp = threefive.Stream('https://futzu.com/xaa.ts')sp.decode_proxy()
  • Pipe to mplayer
$ python3 proxy.py | mplayer -

Stream.show()

|  show(self)|   List programs and streams and info for MPEGTS

Stream.show() Example

>>>> from threefive import Stream>>>> Stream('https://slo.me/plp0.ts').show()
Program: 1040    Service:    fumatic    Provider:   fu-labs    Pcr Pid:    1041[0x411]    Streams:                Pid: 1041[0x411]        Type: 0x1b AVC Video                Pid: 1042[0x412]        Type: 0x3 MP2 Audio                Pid: 1044[0x414]        Type: 0x6 PES Packets/Private Data                Pid: 1045[0x415]        Type: 0x86 SCTE35 DataProgram: 1050    Service:    fancy ˹     Provider:   fu-corp    Pcr Pid:    1051[0x41b]    Streams:                Pid: 1051[0x41b]        Type: 0x1b AVC Video                Pid: 1052[0x41c]        Type: 0x3 MP2 Audio                Pid: 1054[0x41e]        Type: 0x6 PES Packets/Private Data                Pid: 1055[0x41f]        Type: 0x86 SCTE35 Data 

Stream.dump(fname)

|  dump(self, fname)|      Stream.dump dumps all the packets to a file (fname).

Stream.strip_scte35(func=show_cue_stderr)

|  strip_scte35(self, func=show_cue_stderr)|      Stream.strip_scte35 works just like Stream.decode_proxy,|      MPEGTS packets, ( Except the SCTE-35 packets) ,|      are written to stdout after being parsed.|      SCTE-35 cues are printed to stderr.

UUID

A lot of folks have been searching this repo for uuid, I'm not sure if they are looking for uuid in a upid,or a randomly generated uuid for an HLS tag or something else.Here is what I know about it.

>>> from uuid import uuid4, UUID# generate a random uuid>>> uu= uuid4()>>> uuUUID('7ae2e37e-3018-4c4e-8a10-f69d075828b4')# uuid as bytes>>> uu.bytesb'z\xe2\xe3~0\x18LN\x8a\x10\xf6\x9d\x07X(\xb4'# bytes to uuid>>> UUID(bytes=uu.bytes)UUID('7ae2e37e-3018-4c4e-8a10-f69d075828b4')>>> Adrian

python3 uuid

Subscribe to our newsletter