If you need a SCTE-35 parser, threefive is probably what you want.
-
All 2020 SCTE-35 Commands, Descriptors,and Upids are fully supported.
-
Mpegts is Well Supported in the Stream class.
-
Multicast?
HLS?
Custom Upid Handling?
Frame Accurate Preroll timings?
... Oh Yeah.
threefive requires python 3.6+ or pypy3
pypy3 runs threefive Four Times Faster than python3
pip3 install threefive
# for pypy3
pypy3 -m pip install threefive
Release versions are odd.
Unstable testing versions are even.
threefive.version()
returns the version as a string.
threefive.version_number()
returns an int for easy version comparisons.
threefive.decode is a SCTE-35 decoder function with input type auto-detection.
Base64
,Binary
,Hex Strings
,Hex literals
,Integers
,Mpegts files
andMpegts HTTP/HTTPS Streams
SCTE-35 data can be parsed with just one function call.
the arg stuff is the input. if stuff is not set, decode will attempt to read from sys.stdin.buffer.
if stuff is a file, the file data will be read and the type of the data will be autodetected and decoded.
SCTE-35 data is printed in JSON format.
import threefive
stuff = '/DAvAAAAAAAA///wBQb+dGKQoAAZAhdDVUVJSAAAjn+fCAgAAAAALKChijUCAKnMZ1g='
threefive.decode(stuff)
import threefive
payload = b'\xfc0\x11\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\x00\x00\x00O%3\x96'
threefive.decode(payload)
import threefive
stuff = '0XFC301100000000000000FFFFFF0000004F253396'
threefive.decode(stuff)
import threefive
threefive.decode(0XFC301100000000000000FFFFFF0000004F253396)
big_int = 1439737590925997869941740173214217318917816529814
threefive.decode(big_int)
import threefive
threefive.decode('/path/to/mpegts')
import threefive
threefive.decode('https://futzu.com/xaa.ts')
Read from File cue.txt
from threefive import decode
decode('cue.txt')
A threefive SCTE-35 Cue
{
"info_section": {
"table_id": "0xfc",
"section_syntax_indicator": false,
"private": false,
"sap_type": "0x3",
"sap_details": "No Sap Type",
"section_length": 47,
"protocol_version": 0,
"encrypted_packet": false,
"encryption_algorithm": 0,
"pts_adjustment": 0.0,
"cw_index": "0x0",
"tier": "0xfff",
"splice_command_length": 4095,
"splice_command_type": 5,
"descriptor_loop_length": 10,
"crc": "0x10fa4d9e"
},
"command": {
"calculated_length": 20,
"name": "Splice Insert",
"time_specified_flag": true,
"pts_time": 89742.161689,
"break_auto_return": false,
"break_duration": 242.0,
"splice_event_id": 662,
"splice_event_cancel_indicator": false,
"out_of_network_indicator": true,
"program_splice_flag": true,
"duration_flag": true,
"splice_immediate_flag": false,
"unique_program_id": 1,
"avail_num": 0,
"avail_expected": 0
},
"descriptors": [
{
"tag": 0,
"descriptor_length": 8,
"name": "Avail Descriptor",
"identifier": "CUEI",
"provider_avail_id": 0
}
],
"packet_data": {
"pid": "0x135",
"program": 1,
"pcr": 89730.281789,
"pts": 89730.289522
}
}
- src cue.py
- The threefive.Cue class decodes a SCTE35 binary, base64, or hex encoded string.
- threefive.Cue provides several methods to access the parsed data.
>>>> 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 SCTE35 Cues from MPEGTS streams, threefive attempts to include as many of the following as possible.'
- pid of the packet
- program of the pid
- pts of the packet
- pcr of the packet
- call one or more of these methods after decode.
Cue Method | Description |
---|---|
cue.get() | returns cue as a dict |
cue.get_json() | returns cue as a JSON string |
cue.show() | prints cue as JSON |
threefive.Stream(tsdata, show_null = False)
- src stream.py
- The threefive.Stream class parses SCTE35 messages from a file or stream.
- Supports
- Multiple Programs.
- Multiple SCTE35 Streams.
- Multi-Packet PAT, PMT, and SCTE35 tables.
- Constant Data Parsing.
- threefive.Stream is designed to run continuously
Method | Description |
---|---|
Stream.show() | Prints Streams that will be checked for SCTE35 |
Stream.decode(func=show_cue) | Prints SCTE-35 cues for SCTE-35 packets. Accepts an optional function, func, as arg. |
Stream.decode_next() | Returns the next SCTE35 cue as a threefive.Cue instance. |
Stream.decode_program(the_program=None, func=show_cue) | Same as Stream.decode except only packets where program == the_program |
Stream.decode_proxy(func=show_cue) | Same as Stream.decode except raw packets are written to stdout for piping to another program. |
- List programs and streams that will be checked for SCTE35 data.
>>>> from threefive import Stream, version
>>>> version()
'2.2.69'
>>>> with open('video.ts','rb') as tsdata:
.... strm = Stream(tsdata)
.... strm.show()
....
Program:1030
PID: 1034(0x40a) Type: 0x6
PID: 1035(0x40b) Type: 0x86 SCTE35
Program:1100
PID: 1104(0x450) Type: 0x6
PID: 1105(0x451) Type: 0x86 SCTE35
Program:1080
PID: 1084(0x43c) Type: 0x6
import sys
from threefive import Stream
if __name__ =='__main__':
with open(sys.argv[1],'rb') as tsdata:
sp = Stream(tsdata)
sp.decode()
-
Pass in custom function
-
func should match the interface
func(cue)
import sys
import threefive
def display(cue):
print(f'\033[92m{cue.packet_data}\033[00m')
print(f'{cue.command.name}')
def do():
with open(sys.argv[1],'rb') as tsdata:
sp = threefive.Stream(tsdata)
sp.decode(func = display)
if __name__ == '__main__':
do()
- Stream.decode_next returns the next SCTE35 cue as a threefive.Cue instance.
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()
- Use Stream.decode_program() instead of Stream.decode() to decode SCTE-35 from packets where program == the_program
import threefive
with open('../35.ts','rb') as tsdata:
threefive.Stream(tsdata).decode_program(1)
-
Writes all packets to sys.stdout.
-
Writes scte35 data to sys.stderr.
import threefive
with open('vid.ts','rb') as tsdata:
sp = threefive.Stream(tsdata)
sp.proxy_decode()
- Pipe to mplayer
$ python3 proxy.py | mplayer -
Speak up. I want to hear what you have to say.
If threefive doesn't work as expected,
or if you find a bug ,
or if you have feature request,
please open an issue.
If you want help resolving a video parsing issue, a sample of the video is required .