Pythonic SCTE35
Project description
Install | Fast Start | Cue Class | Stream Class | Examples | ffmpeg and threefive | x9k3 | gumd | m3ufu| effbot
threefive is a SCTE35 parser.
Before we get into all that, I have a brief statement.
I am still looking for my Watergate, my defining scandal.
I dont mean to keep beating the same drum,
but I still say that subliminal messages
will be very effective over the Internet.
It will take years before we see any laws
against shttps.
(The first "s" is for subliminal)
-
All 2022 SCTE35 Commands and Descriptors and Upids
-
ffmpeg and SCTE35 and Stream Type 0x6 bin data and threefive
-
Issues and `Bugs and Feature Requests No forms man, just open an issue and tell me what you need.
Requirements
- threefive requires
- pypy3 or python 3.6+
- new_reader
- optional dependencies:
- pyaes If you want AES decryption for HLS segments.
Install
python3 -mpip install threefive
# and / or
pypy3 -m pip install threefive
To install the optional dependencies
python3 -mpip install threefive[all]
# and / or
pypy3 -mpip install threefive[all]
Versions and Releases
Python 3.10.6 (main, Aug 10 2022, 11:19:32) [GCC 12.1.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from threefive import version
>>> version()
'2.3.45'
>>>
- I do Rolling Releases
- Release versions are odd.
- Unstable testing versions are even.
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 device
ip link set <dev> multicast on allmulticast on
ethtool -G <dev> rx 4096
sysctl -w net.core.rmem_default=5000000
sysctl -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 sys
from 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 sys
import threefive
def 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 sys
import threefive
def 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 threefive
threefive.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 threefive
sp = 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 Data
Program: 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()
>>> uu
UUID('7ae2e37e-3018-4c4e-8a10-f69d075828b4')
# uuid as bytes
>>> uu.bytes
b'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
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
Built Distribution
Hashes for threefive-2.3.49-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 80824a352b8bf0631399a8da4299db704e13276d09815fa6e7c7e0477f431bbc |
|
MD5 | ff65683fcc19cc39d9eed9c9cfc244ac |
|
BLAKE2b-256 | d23cbbeeb1490c469d29a8cc4840931a223f43ff0f4a40dedaea2bc7e0aba292 |