The Undisputed Heavyweight Champion of SCTE-35.
Project description
threefive is the highest rated SCTE-35 parser. Ever.
Parses
SCTE-35 from multiple streams in MPEGTS
and Multiple Program Transport Streams
Parses
SCTE-35 from Cues encoded inBase64
, Bytes
, Hex
, Integers
.
Parses
SCTE-35 from files
, http(s)
, Multicast
, UDP
and even stdin
( you can pipe to it).
Parses
SCTE-35 from streams converted to bin data
( type 0x06 ) by ffmpeg
.
Heads Up!
Signifigant changes coming in v2.4.61 ( that you probably won't notice)
Vars shown in seconds and ticks, will now only be shown in seconds
The SpliceSchedule command has been removed
The AudioDescriptor Descriptor has been removed
- Using ticks and seconds causes tons of problems and I'm over it.
- In four years I have never seen a SpliceSchedule or an AudioDescriptor in the Wild.
- Show me one and I'll put it back.
v2.4.61 is out!
Latest threefive version is 2
.4
.61
- threefive
2
.4
.61
- Cyclomatic complexity score: A (
1.9603174603174602
) - 1814 lines of code, 924 lines of comments
dropped
ticksdropped
SpliceScheduledropped
AudioDescriptorfixed
Cue.encode()
- Cyclomatic complexity score: A (
- threefive
2
.4
.57
Fix for multicast( new_reader)- python has been making changes to the socket lib.
- this fixes multicast in new_reader and is backward compatible.
- threefive
2
.4
.55
has a CRITICAL FIX related to ffmpeg.
Documentation
-
SCTE-35 on the command line
-
Trigger on SCTE-35 events using threefive.Stream
-
SCTE-35 code Examples
-
Parse SCTE-35 programmatically with a few lines of code
-
Issues and Featue Requests and Assorted Comments.
Install
- install threefive with pip
- threefive has two third party package dependencies and I wrote one of them.
- pip will automatically install PyAES and new_reader
python3 -mpip install --upgrade threefive
Versions and Releases
Every time I fix a bug or add a feature, I do a new release.
This makes tracking down bugs and stuff much easier.
Keep up folks.
a@slow:~/threefive$ threefive version
2.4.41
a@slow:~/threefive$
- Release versions are odd.
- Unstable testing versions are even.
Cue Class
- src cue.py
- The threefive.Cue class decodes a SCTE35 binary, base64, or hex encoded string.
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
- 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'
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.
-
threefive tries to include pid, program, anf pts of the SCTE-35 packet.
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
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
"""
Stream.decode_next example.
decode_next returns the Cue every time a Cue is found.
This uses a while loop to pull the Cues from a mpegts stream.
When a Cue is found, if it's a Time Signal,
cue.command.command_type=6, print Cue.command.
You can filter on any var in the SCTE-35 Cue.
"""
import sys
import threefive
from new_reader import reader
def do():
arg = sys.argv[1]
with reader(arg) as tsdata:
st = threefive.Stream(tsdata)
while True:
cue = st.decode_next()
if not cue:
return False
if cue:
if cue.command.command_type ==6:
print(cue.command)
if __name__ == "__main__":
do()
-
Stream.proxy(func = show_cue)
-
Writes all packets to sys.stdout.
-
Writes scte35 data to sys.stderr.
-
| decode(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.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()
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
Need to verify your splice points?
-
Try cue2vtt.py in the examples.
- cue2vtt.py creates webvtt subtitles out of SCTE-35 Cue data
-
use it like this
pypy3 cue2vtt.py video.ts | mplayer video.ts -sub -
threefive is now addressable TV compatible
"tag": 2,
"descriptor_length": 31,
"name": "Segmentation Descriptor",
"identifier": "CUEI",
"components": [],
"segmentation_event_id": "0x065eff",
"segmentation_event_cancel_indicator": false,
"segmentation_event_id_compliance_indicator": true,
"program_segmentation_flag": true,
"segmentation_duration_flag": false,
"delivery_not_restricted_flag": true,
"segmentation_message": "Call Ad Server", < --- Boom
"segmentation_upid_type": 12,
"segmentation_upid_type_name": "MPU",
"segmentation_upid_length": 16,
"segmentation_upid": {
"format_identifier": "ADFR", <--- Boom
"private_data": "0x0133f10134b04f065e060220",
"version": 1, <---- Boom
"channel_identifier": "0x33f1", <---- Boom
"date": 20230223, <---- Boom
"break_code": 1630, <---- Boom
"duration": "0x602" <---- Boom
},
"segmentation_type_id": 2, <---- Boom
"segment_num": 0,
"segments_expected": 0
},
Custom charsets for UPIDS aka upids.charset
Specify a charset for Upid data by setting threefive.upids.charset
issue #55
- default charset is ascii
- python charsets info Here
- setting charset to None will return raw bytes.
Example Usage:
>>> from threefive import Cue,upids
>>> i="/DBKAAAAAAAAAP/wBQb+YtC8/AA0AiZDVUVJAAAD6X/CAAD3W3ACEmJibG5kcHBobkQCAsGDpQIAAAAAAAEKQ1VFSRSAIyowMljRk9c="
>>> upids.charset
'ascii'
>>> cue=Cue(i)
>>> cue.decode()
ascii
True
>>> cue.descriptors[0].segmentation_upid
'bblndpphnD\x02\x02���\x02\x00\x00'
>>> upids.charset="utf16"
>>> cue.decode()
utf16
True
>>> cue.descriptors[0].segmentation_upid
'扢湬灤桰䑮Ȃ菁ʥ\x00'
Custom Private Splice Descriptors ( new! )
threefive now supports custom private splice descriptors, right out the box.
- The first byte of the descriptor is read as an int for the Descriptor tag
- The second byte is read as an int for the desciptor length
- The next four bytes are read as ASCII for the Identifier
- remanining bytes are returned as private data
from threefive import Cue, TimeSignal
from threefive.descriptors import SpliceDescriptor
- make a Cue
c = Cue()
- add a Time Signal
c.command = TimeSignal()
c.command.time_specified_flag=True
c.command.pts_time=1234.567890
- add Splice Descriptor
sd = SpliceDescriptor()
sd.tag = 47
sd.identifier ='fufu'
sd.private_data = b'threefive kicks ass'
c.descriptors.append(sd)
- encode
c.encode()
'/DAvAAAAAAAAAP/wBQb+Bp9rxgAZLxdmdWZ1dGhyZWVmaXZlIGtpY2tzIGFzc1m+EsU='
- show
c.show()
{
"info_section": {
"table_id": "0xfc",
"section_syntax_indicator": false,
"private": false,
"sap_type": "0x03",
"sap_details": "No Sap Type",
"section_length": 47,
"protocol_version": 0,
"encrypted_packet": false,
"encryption_algorithm": 0,
"pts_adjustment_ticks": 0,
"cw_index": "0x0",
"tier": "0xfff",
"splice_command_length": 5,
"splice_command_type": 6,
"descriptor_loop_length": 25,
"crc": "0x59be12c5"
},
"command": {
"command_length": 5,
"command_type": 6,
"name": "Time Signal",
"time_specified_flag": true,
"pts_time": 1234.56789,
"pts_time_ticks": 111111110
},
"descriptors": [
{
"tag": 47,
"descriptor_length": 23,
"identifier": "fufu",
"private_data": [
116,
104,
114,
101,
101,
102,
105,
118,
101,
32,
107,
105,
99,
107,
115,
32,
97,
115,
115
]
}
]
}
- the custom Splice Descriptor
c.descriptors[0]
{'tag': 47, 'descriptor_length': 23, 'name': None, 'identifier': 'fufu', 'bites': None, 'provider_avail_id': None, 'components': None, 'private_data': b'threefive kicks ass'}
- Cool dictionaary comprehension to print the Splice Descriptor with only relevant values
{print(f'{k} = {v}') for k,v in vars(c.descriptors[0]).items() if v is not None}
tag = 47
descriptor_length = 23
identifier = fufu
private_data = b'threefive kicks ass'
Powered by threefive
⚡ sideways inject SCTE-35 into HLS via manifest manipulation.
⚡ adbreak2 a cli tool that quickly and easily generates SCTE-35 Cues for HLS and stuff.
⚡ Ultra Mega Zoom Zoom ABR HLS segmenter and SCTE-35 inserter.
⚡ POIS Server is Super Cool.
⚡ bpkio-cli: A command line interface to the broadpeak.io APIs.
⚡ x9k3: SCTE-35 HLS Segmenter and Cue Inserter.
⚡ amt-play uses x9k3.
⚡ m3ufu: SCTE-35 m3u8 Parser.
⚡ six2scte35: ffmpeg changes SCTE-35 stream type to 0x06 bin data, six2scte35 changes it back.
⚡ SuperKabuki: SCTE-35 Packet Injection.
⚡ showcues m3u8 SCTE-35 parser.
⚡threefive online SCTE-35 Encoder
⚡ threefive online SCTE-35 parser
threefive | more
⚡ Diagram of a threefive SCTE-35 Cue.
⚡ ffmpeg and threefive and SCTE35 and Stream Type 0x6 bin data.
⚡ Issues and Bugs and Feature Requests will be considered. Please don't make me regret it.
NEW!
threefive now has experimental DVB DAS Support ETSI TS 103 752-1
SCTE-35 Parsing Shoot Out:
- The challenge is to parse 14,862 SCTE-35 Cues from an MPEGTS as fast as possible.
threefive + Python3
VS.threefive + pypy3
VS.tsduck in C++
VS.cuei in Go
.
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.4.61-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | c81c791925c40f91d40514ce3dc64b0ebd1dfed904ab607e2d700a907e2246bd |
|
MD5 | de5252158e5a239b02a386f4030adec6 |
|
BLAKE2b-256 | 30e89c991e827261e2f29b2450ec997613afd1c52e37e35ca19b2ffb796c0abc |