Q. How many lines of code doe it take to parse SCTE35 from Mpegts?
A. Two.
from threefive import decode
decode("https://futzu.com/xaa.ts")-
Supports All 2020 SCTE-35
CommandsandDescriptorsandUpids. -
ffmpegandSCTE35andStream Type 0x6 bin dataand threefive -
A threefive SCTE-35 Cue.What's included.
-
[35decode
35decode https://futzu.com/xaa.ts, a classy cli tool](https://github.com/futzu/threefive/blob/master/examples/35decode)
-
Heads Up. New output format forthreefive.Stream.show()just pushed. -
HLS?Custom Upid Handling?Frame Accurate Preroll timings?... Yes. -
Encodingtoo withExamples -
IssuesandBugsandFeature RequestsNo forms man, just open an issue and tell me what you need. -
Heads Up. New output format forthreefive.Stream.show()just pushed.
-
Diagram of a threefive SCTE-35 Cue
-
IssuesandBugsandFeatureRequests No forms man, just open an issue.
- threefive requires pypy3 or python 3.6+
- (pypy3 runs threefive 4x Faster than python3 but uses a lot more memory)
- threefive 2.3.02+ requires crcmod for encoding and pyaes for decrypting.
pip3 install threefive
# for pypy3
pypy3 -m pip install threefiveRelease 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.
the tsduck user's manual is longer than threefive's source code.
threefive.decode is a SCTE-35 decoder function with input type auto-detection.
Base64,Binary,Hex Strings,Hex literals,Integers,Mpegts filesandMpegts HTTP/HTTPS StreamsSCTE-35 data can be parsed with just one function call.
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')import threefive
threefive.decode('udp://10.0.0.1:555')import threefive
threefive.decode('udp://@239.35.0.35:1234')- 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()
TrueAfter 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 from Mpegts.
- Supports:
- File and Http(s) and Udp and Multicast protocols.
- 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
>>>> 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 Dataimport sys
from threefive import Stream
>>>> Stream('plp0.ts').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():
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
threefive.Stream('35.ts').decode_program(1)-
Writes all packets to sys.stdout.
-
Writes scte35 data to sys.stderr.
import threefive
sp = threefive.Stream('https://futzu.com/xaa.ts')
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.