The Undisputed Heavyweight Champion of SCTE-35.
Project description
It's not just me saying threefive is the best SCTE-35 parser.
threefive is the highest rated SCTE-35 parser. Ever.
cli tool and library for encoding and decoding SCTE-35.
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
.
You haven't seen an online SCTE-35 Encoder before, have you?
threefive online SCTE-35 parser
new stuff in threefive
-
Latest threefive release is
2
.4
.29
, -
threefive supports the latest SCTE-35 specification
SCTE-35 2023r1
-
threefive/go is now cuei
threefive cli tool now accepts version, show and pts keywords.
version
a@fu:~$ threefive version
2.4.25
show
a@fu:~$ threefive show f10.ts
Program: 1
Service: Service01
Provider: FFmpeg
Pid: 4096
Pcr Pid: 256
Streams:
Pid: 256[0x100] Type: 0x1b AVC Video
Pid: 257[0x101] Type: 0xf AAC Audio
Pid: 258[0x102] Type: 0x6 PES Packets/Private Data
Pid: 259[0x103] Type: 0x6 PES Packets/Private Data
Pid: 260[0x104] Type: 0x15 ID3 Timed Meta Data
pts
a@fu:~$ threefive pts f10.ts
1-> 1.466667
1-> 1.6
1-> 1.533333
1-> 1.533333
1-> 1.533333
1-> 1.5
1-> 1.566667
1-> 1.733333
1-> 1.733333
threefive is now addressable TVnew stuff in thr 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
},
threefive's code is cleaner than your dishes
A cyclomatic coimplesity score under 15 is considered good.
threefive's cyclomatic complexity is 1.968
250 blocks (classes, functions, methods) analyzed.
Average complexity: A (1.968)
a@fu:~/.local/lib/pypy3.9/site-packages$ radon cc -sa -o SCORE --md threefive/*.py
Filename | Name | Type | Start:End Line | Complexity | Classification |
---|---|---|---|---|---|
threefive/base.py | SCTE35Base.kv_clean | M | 68:84 | 3 | A |
threefive/base.py | SCTE35Base.load | M | 92:100 | 3 | A |
threefive/base.py | SCTE35Base._chk_var | M | 102:115 | 3 | A |
threefive/base.py | SCTE35Base | C | 9:115 | 2 | A |
threefive/base.py | SCTE35Base.as_hms | M | 35:46 | 2 | A |
threefive/base.py | SCTE35Base._chk_nbin | M | 87:90 | 2 | A |
threefive/base.py | SCTE35Base.repr | M | 17:18 | 1 | A |
threefive/base.py | SCTE35Base.as_90k | M | 21:25 | 1 | A |
threefive/base.py | SCTE35Base.as_ticks | M | 28:32 | 1 | A |
threefive/base.py | SCTE35Base.fix_hex | M | 49:53 | 1 | A |
threefive/base.py | SCTE35Base.get | M | 55:59 | 1 | A |
threefive/base.py | SCTE35Base.get_json | M | 61:66 | 1 | A |
threefive/bitn.py | BitBin | C | 9:91 | 2 | A |
threefive/bitn.py | BitBin.as_int | M | 30:38 | 2 | A |
threefive/bitn.py | BitBin.as_charset | M | 48:60 | 2 | A |
threefive/bitn.py | NBin | C | 94:188 | 2 | A |
threefive/bitn.py | NBin.add_int | M | 128:135 | 2 | A |
threefive/bitn.py | NBin.reserve | M | 165:174 | 2 | A |
threefive/bitn.py | NBin.zeroed | M | 182:188 | 2 | A |
threefive/bitn.py | BitBin.init | M | 17:20 | 1 | A |
threefive/bitn.py | BitBin.as_90k | M | 22:28 | 1 | A |
threefive/bitn.py | BitBin.as_hex | M | 40:46 | 1 | A |
threefive/bitn.py | BitBin.as_bytes | M | 63:70 | 1 | A |
threefive/bitn.py | BitBin.as_flag | M | 72:76 | 1 | A |
threefive/bitn.py | BitBin.forward | M | 78:83 | 1 | A |
threefive/bitn.py | BitBin.negative_shift | M | 85:91 | 1 | A |
threefive/bitn.py | NBin.init | M | 102:105 | 1 | A |
threefive/bitn.py | NBin.nbits2bites | M | 107:116 | 1 | A |
threefive/bitn.py | NBin.add_bites | M | 118:123 | 1 | A |
threefive/bitn.py | NBin.add_90k | M | 137:144 | 1 | A |
threefive/bitn.py | NBin.add_hex | M | 146:154 | 1 | A |
threefive/bitn.py | NBin.add_flag | M | 156:163 | 1 | A |
threefive/bitn.py | NBin.forward | M | 176:180 | 1 | A |
threefive/commands.py | TimeSignal._encode_splice_time | M | 140:153 | 6 | B |
threefive/commands.py | SpliceInsert.decode | M | 179:193 | 4 | A |
threefive/commands.py | SpliceInsert.encode | M | 232:245 | 4 | A |
threefive/commands.py | SpliceInsert._encode_break | M | 257:269 | 4 | A |
threefive/commands.py | TimeSignal | C | 95:153 | 3 | A |
threefive/commands.py | SpliceInsert | C | 156:286 | 3 | A |
threefive/commands.py | SpliceSchedule | C | 289:352 | 3 | A |
threefive/commands.py | SpliceCommand | C | 8:36 | 2 | A |
threefive/commands.py | BandwidthReservation | C | 39:50 | 2 | A |
threefive/commands.py | PrivateCommand | C | 55:81 | 2 | A |
threefive/commands.py | SpliceNull | C | 84:92 | 2 | A |
threefive/commands.py | TimeSignal._splice_time | M | 126:138 | 2 | A |
threefive/commands.py | SpliceInsert._decode_break | M | 195:204 | 2 | A |
threefive/commands.py | SpliceSchedule.decode | M | 342:352 | 2 | A |
threefive/commands.py | SpliceCommand.init | M | 13:17 | 1 | A |
threefive/commands.py | SpliceCommand.decode | M | 19:20 | 1 | A |
threefive/commands.py | SpliceCommand._set_len | M | 24:29 | 1 | A |
threefive/commands.py | SpliceCommand.encode | M | 31:36 | 1 | A |
threefive/commands.py | BandwidthReservation.init | M | 44:47 | 1 | A |
threefive/commands.py | BandwidthReservation.decode | M | 49:50 | 1 | A |
threefive/commands.py | PrivateCommand.init | M | 60:64 | 1 | A |
threefive/commands.py | PrivateCommand.decode | M | 66:73 | 1 | A |
threefive/commands.py | PrivateCommand.encode | M | 75:81 | 1 | A |
threefive/commands.py | SpliceNull.init | M | 89:92 | 1 | A |
threefive/commands.py | TimeSignal.init | M | 100:106 | 1 | A |
threefive/commands.py | TimeSignal.decode | M | 108:115 | 1 | A |
threefive/commands.py | TimeSignal.encode | M | 117:124 | 1 | A |
threefive/commands.py | SpliceInsert.init | M | 161:177 | 1 | A |
threefive/commands.py | SpliceInsert._decode_event | M | 206:214 | 1 | A |
threefive/commands.py | SpliceInsert._decode_flags | M | 216:225 | 1 | A |
threefive/commands.py | SpliceInsert._decode_unique_avail | M | 227:230 | 1 | A |
threefive/commands.py | SpliceInsert._encode_event | M | 247:255 | 1 | A |
threefive/commands.py | SpliceInsert._encode_flags | M | 271:281 | 1 | A |
threefive/commands.py | SpliceInsert._encode_unique_avail | M | 283:286 | 1 | A |
threefive/commands.py | SpliceSchedule.init | M | 333:340 | 1 | A |
threefive/crc.py | _bytecrc | F | 16:22 | 2 | A |
threefive/crc.py | _mk_table | F | 25:28 | 2 | A |
threefive/crc.py | crc32 | F | 31:41 | 2 | A |
threefive/cue.py | Cue._mk_bits | M | 128:154 | 7 | B |
threefive/cue.py | Cue.load_descriptors | M | 325:344 | 6 | B |
threefive/cue.py | Cue.load | M | 275:293 | 5 | A |
threefive/cue.py | Cue.load_command | M | 307:323 | 5 | A |
threefive/cue.py | Cue.get | M | 92:106 | 4 | A |
threefive/cue.py | Cue | C | 15:344 | 3 | A |
threefive/cue.py | Cue._descriptor_loop | M | 78:90 | 3 | A |
threefive/cue.py | Cue._unloop_descriptors | M | 259:273 | 3 | A |
threefive/cue.py | Cue.init | M | 45:56 | 2 | A |
threefive/cue.py | Cue.decode | M | 63:76 | 2 | A |
threefive/cue.py | Cue.get_descriptors | M | 108:113 | 2 | A |
threefive/cue.py | Cue.fix_bad_b64 | M | 123:126 | 2 | A |
threefive/cue.py | Cue._set_splice_command | M | 179:192 | 2 | A |
threefive/cue.py | Cue.encode | M | 210:235 | 2 | A |
threefive/cue.py | Cue.load_info_section | M | 295:305 | 2 | A |
threefive/cue.py | Cue.repr | M | 58:59 | 1 | A |
threefive/cue.py | Cue.get_json | M | 115:120 | 1 | A |
threefive/cue.py | Cue._mk_descriptors | M | 156:166 | 1 | A |
threefive/cue.py | Cue.mk_info_section | M | 168:177 | 1 | A |
threefive/cue.py | Cue.show | M | 194:198 | 1 | A |
threefive/cue.py | Cue.to_stderr | M | 200:206 | 1 | A |
threefive/cue.py | Cue.encode_as_int | M | 237:242 | 1 | A |
threefive/cue.py | Cue.encode_as_hex | M | 244:249 | 1 | A |
threefive/cue.py | Cue._encode_crc | M | 251:257 | 1 | A |
threefive/decode.py | _read_stuff | F | 27:40 | 3 | A |
threefive/decode.py | decode | F | 43:83 | 3 | A |
threefive/descriptors.py | SegmentationDescriptor._encode_segmentation | M | 367:385 | 4 | A |
threefive/descriptors.py | k_by_v | F | 10:17 | 3 | A |
threefive/descriptors.py | SegmentationDescriptor | C | 242:401 | 3 | A |
threefive/descriptors.py | SegmentationDescriptor._decode_segmentation | M | 299:311 | 3 | A |
threefive/descriptors.py | SegmentationDescriptor._decode_segments | M | 313:334 | 3 | A |
threefive/descriptors.py | SpliceDescriptor | C | 20:83 | 2 | A |
threefive/descriptors.py | SpliceDescriptor.parse_tag_and_len | M | 39:48 | 2 | A |
threefive/descriptors.py | SpliceDescriptor.parse_id | M | 50:59 | 2 | A |
threefive/descriptors.py | SpliceDescriptor.encode | M | 67:75 | 2 | A |
threefive/descriptors.py | AudioDescriptor | C | 86:144 | 2 | A |
threefive/descriptors.py | AudioDescriptor.decode | M | 119:130 | 2 | A |
threefive/descriptors.py | AudioDescriptor.encode | M | 132:144 | 2 | A |
threefive/descriptors.py | AvailDescriptor | C | 147:170 | 2 | A |
threefive/descriptors.py | DtmfDescriptor | C | 173:206 | 2 | A |
threefive/descriptors.py | DtmfDescriptor.encode | M | 194:206 | 2 | A |
threefive/descriptors.py | TimeDescriptor | C | 209:239 | 2 | A |
threefive/descriptors.py | SegmentationDescriptor.decode | M | 274:285 | 2 | A |
threefive/descriptors.py | SegmentationDescriptor._decode_flags | M | 287:297 | 2 | A |
threefive/descriptors.py | SegmentationDescriptor.encode | M | 336:352 | 2 | A |
threefive/descriptors.py | SegmentationDescriptor._encode_flags | M | 354:365 | 2 | A |
threefive/descriptors.py | SegmentationDescriptor._encode_segments | M | 387:401 | 2 | A |
threefive/descriptors.py | splice_descriptor | F | 414:423 | 1 | A |
threefive/descriptors.py | SpliceDescriptor.init | M | 27:37 | 1 | A |
threefive/descriptors.py | SpliceDescriptor.decode | M | 61:65 | 1 | A |
threefive/descriptors.py | SpliceDescriptor._encode_id | M | 77:83 | 1 | A |
threefive/descriptors.py | AudioDescriptor.init | M | 91:96 | 1 | A |
threefive/descriptors.py | AudioDescriptor._decode_comp | M | 98:109 | 1 | A |
threefive/descriptors.py | AudioDescriptor._encode_comp | M | 112:117 | 1 | A |
threefive/descriptors.py | AvailDescriptor.init | M | 152:155 | 1 | A |
threefive/descriptors.py | AvailDescriptor.decode | M | 157:162 | 1 | A |
threefive/descriptors.py | AvailDescriptor.encode | M | 164:170 | 1 | A |
threefive/descriptors.py | DtmfDescriptor.init | M | 178:183 | 1 | A |
threefive/descriptors.py | DtmfDescriptor.decode | M | 185:192 | 1 | A |
threefive/descriptors.py | TimeDescriptor.init | M | 214:220 | 1 | A |
threefive/descriptors.py | TimeDescriptor.decode | M | 222:229 | 1 | A |
threefive/descriptors.py | TimeDescriptor.encode | M | 231:239 | 1 | A |
threefive/descriptors.py | SegmentationDescriptor.init | M | 247:272 | 1 | A |
threefive/encode.py | mk_splice_insert | F | 50:115 | 3 | A |
threefive/encode.py | mk_time_signal | F | 25:47 | 2 | A |
threefive/encode.py | mk_splice_null | F | 13:22 | 1 | A |
threefive/packetdata.py | PacketData | C | 8:47 | 3 | A |
threefive/packetdata.py | PacketData._mk_timestamp | M | 24:27 | 2 | A |
threefive/packetdata.py | PacketData.mk_pcr | M | 29:37 | 2 | A |
threefive/packetdata.py | PacketData.mk_pts | M | 39:47 | 2 | A |
threefive/packetdata.py | PacketData.init | M | 15:21 | 1 | A |
threefive/section.py | SpliceInfoSection._encode_encrypted | M | 114:124 | 3 | A |
threefive/section.py | SpliceInfoSection._encode_pts_adjustment | M | 126:134 | 3 | A |
threefive/section.py | SpliceInfoSection._encode_splice_command | M | 152:162 | 3 | A |
threefive/section.py | SpliceInfoSection | C | 17:182 | 2 | A |
threefive/section.py | SpliceInfoSection.decode | M | 43:66 | 2 | A |
threefive/section.py | SpliceInfoSection._encode_sap | M | 89:96 | 2 | A |
threefive/section.py | SpliceInfoSection._encode_section_length | M | 98:104 | 2 | A |
threefive/section.py | SpliceInfoSection._encode_protocol_version | M | 106:112 | 2 | A |
threefive/section.py | SpliceInfoSection._encode_cw_index | M | 136:142 | 2 | A |
threefive/section.py | SpliceInfoSection._encode_tier | M | 144:150 | 2 | A |
threefive/section.py | SpliceInfoSection.init | M | 23:41 | 1 | A |
threefive/section.py | SpliceInfoSection._encode_table_id | M | 68:73 | 1 | A |
threefive/section.py | SpliceInfoSection._encode_section_syntax_indicator | M | 75:80 | 1 | A |
threefive/section.py | SpliceInfoSection._encode_private_flag | M | 82:87 | 1 | A |
threefive/section.py | SpliceInfoSection.encode | M | 164:182 | 1 | A |
threefive/segment.py | Segment.decode | M | 119:136 | 6 | B |
threefive/segment.py | Segment.init | M | 56:73 | 4 | A |
threefive/segment.py | Segment | C | 15:136 | 3 | A |
threefive/segment.py | Segment.show_cue | M | 109:117 | 2 | A |
threefive/segment.py | Segment.repr | M | 75:76 | 1 | A |
threefive/segment.py | Segment._mk_tmp | M | 78:80 | 1 | A |
threefive/segment.py | Segment._aes_get_key | M | 82:84 | 1 | A |
threefive/segment.py | Segment._aes_decrypt | M | 86:93 | 1 | A |
threefive/segment.py | Segment._add_cue | M | 95:100 | 1 | A |
threefive/segment.py | Segment.shushed | M | 102:107 | 1 | A |
threefive/smoketest.py | smoke | F | 33:55 | 4 | A |
threefive/smoketest.py | _decode_test | F | 24:30 | 2 | A |
threefive/stream.py | Stream._parse_scte35 | M | 523:543 | 7 | B |
threefive/stream.py | Stream._parse_sdt | M | 545:579 | 7 | B |
threefive/stream.py | Stream._parse_tables | M | 460:475 | 6 | B |
threefive/stream.py | Stream._parse_pmt | M | 602:627 | 6 | B |
threefive/stream.py | Stream._find_start | M | 171:187 | 5 | A |
threefive/stream.py | Stream.decode | M | 225:238 | 5 | A |
threefive/stream.py | Stream.decode_fu | M | 246:260 | 5 | A |
threefive/stream.py | Stream.show_pts | M | 318:334 | 5 | A |
threefive/stream.py | Stream._parse_pts | M | 425:442 | 5 | A |
threefive/stream.py | Stream.proxy | M | 286:298 | 4 | A |
threefive/stream.py | Stream.show | M | 300:316 | 4 | A |
threefive/stream.py | Stream._parse_pat | M | 581:600 | 4 | A |
threefive/stream.py | ProgramInfo | C | 56:89 | 3 | A |
threefive/stream.py | ProgramInfo.show | M | 70:89 | 3 | A |
threefive/stream.py | Stream | C | 129:660 | 3 | A |
threefive/stream.py | Stream._parse_cc | M | 415:423 | 3 | A |
threefive/stream.py | Stream._parse | M | 487:495 | 3 | A |
threefive/stream.py | Pids | C | 92:106 | 2 | A |
threefive/stream.py | Maps | C | 109:126 | 2 | A |
threefive/stream.py | Stream.init | M | 144:166 | 2 | A |
threefive/stream.py | Stream.pid2prgm | M | 189:197 | 2 | A |
threefive/stream.py | Stream.pid2pts | M | 199:207 | 2 | A |
threefive/stream.py | Stream.pid2pcr | M | 209:217 | 2 | A |
threefive/stream.py | Stream._mk_pkts | M | 240:243 | 2 | A |
threefive/stream.py | Stream.decode_start_time | M | 336:343 | 2 | A |
threefive/stream.py | Stream._has_pts | M | 381:384 | 2 | A |
threefive/stream.py | Stream._split_by_idx | M | 409:413 | 2 | A |
threefive/stream.py | Stream._parse_payload | M | 450:458 | 2 | A |
threefive/stream.py | Stream._parse_info | M | 477:485 | 2 | A |
threefive/stream.py | Stream._chk_partial | M | 497:500 | 2 | A |
threefive/stream.py | Stream._same_as_last | M | 502:506 | 2 | A |
threefive/stream.py | Stream._section_incomplete | M | 508:513 | 2 | A |
threefive/stream.py | Stream._parse_cue | M | 515:521 | 2 | A |
threefive/stream.py | Stream._parse_program_streams | M | 629:642 | 2 | A |
threefive/stream.py | Stream._set_scte35_pids | M | 654:660 | 2 | A |
threefive/stream.py | no_op | F | 32:37 | 1 | A |
threefive/stream.py | show_cue | F | 40:45 | 1 | A |
threefive/stream.py | show_cue_stderr | F | 48:53 | 1 | A |
threefive/stream.py | ProgramInfo.init | M | 63:68 | 1 | A |
threefive/stream.py | Pids.init | M | 100:106 | 1 | A |
threefive/stream.py | Maps.init | M | 119:126 | 1 | A |
threefive/stream.py | Stream.repr | M | 168:169 | 1 | A |
threefive/stream.py | Stream.iter_pkts | M | 219:223 | 1 | A |
threefive/stream.py | Stream.decode_next | M | 262:267 | 1 | A |
threefive/stream.py | Stream.decode_program | M | 269:275 | 1 | A |
threefive/stream.py | Stream.decode_pids | M | 277:284 | 1 | A |
threefive/stream.py | Stream._mk_packet_data | M | 345:350 | 1 | A |
threefive/stream.py | Stream.as_90k | M | 353:357 | 1 | A |
threefive/stream.py | Stream._pusi_flag | M | 360:361 | 1 | A |
threefive/stream.py | Stream._afc_flag | M | 364:365 | 1 | A |
threefive/stream.py | Stream._pcr_flag | M | 368:369 | 1 | A |
threefive/stream.py | Stream._spi_flag | M | 372:373 | 1 | A |
threefive/stream.py | Stream._pts_flag | M | 376:378 | 1 | A |
threefive/stream.py | Stream._parse_length | M | 387:391 | 1 | A |
threefive/stream.py | Stream._parse_pid | M | 394:399 | 1 | A |
threefive/stream.py | Stream._parse_program | M | 402:406 | 1 | A |
threefive/stream.py | Stream.pts | M | 444:448 | 1 | A |
threefive/stream.py | Stream._parse_stream_type | M | 644:652 | 1 | A |
threefive/stuff.py | print2 | F | 8:12 | 1 | A |
threefive/upids.py | upid_encoder | F | 139:178 | 5 | A |
threefive/upids.py | UpidDecoder._decode_eidr | M | 39:48 | 3 | A |
threefive/upids.py | UpidDecoder.decode | M | 104:136 | 3 | A |
threefive/upids.py | _encode_mid | F | 203:211 | 2 | A |
threefive/upids.py | _encode_umid | F | 226:229 | 2 | A |
threefive/upids.py | _encode_uri | F | 232:235 | 2 | A |
threefive/upids.py | UpidDecoder | C | 14:136 | 2 | A |
threefive/upids.py | UpidDecoder._decode_mid | M | 53:72 | 2 | A |
threefive/upids.py | UpidDecoder._decode_mpu | M | 74:88 | 2 | A |
threefive/upids.py | UpidDecoder._decode_umid | M | 90:96 | 2 | A |
threefive/upids.py | _encode_air_id | F | 181:182 | 1 | A |
threefive/upids.py | _encode_atsc | F | 185:190 | 1 | A |
threefive/upids.py | _encode_eidr | F | 193:196 | 1 | A |
threefive/upids.py | _encode_isan | F | 199:200 | 1 | A |
threefive/upids.py | _encode_mpu | F | 215:219 | 1 | A |
threefive/upids.py | _encode_no | F | 222:223 | 1 | A |
threefive/upids.py | UpidDecoder.init | M | 20:24 | 1 | A |
threefive/upids.py | UpidDecoder._decode_air_id | M | 26:27 | 1 | A |
threefive/upids.py | UpidDecoder._decode_atsc | M | 29:36 | 1 | A |
threefive/upids.py | UpidDecoder._decode_isan | M | 50:51 | 1 | A |
threefive/upids.py | UpidDecoder._decode_uri | M | 98:99 | 1 | A |
threefive/upids.py | UpidDecoder._decode_no | M | 101:102 | 1 | A |
SCTE-35 code examples
Documentation (click a topic to expand)
Supported Platforms
- threefive is expected to work on any platform that runs python3.6 and up.
- There are no known platform specific issues.
Requirements
-
threefive requires
- pypy3 or python 3.6+ (pypy3 runs threefive 2-3 times faster than python 3.10)
- new_reader
- pyaes
Versions and Releases
Every time I fix a bug or add a feature, I do a new release. I only support the latest version. Stay up with me.
a@fu:~$ pypy3
Python 3.9.17 (7.3.12+dfsg-1, Jun 16 2023, 18:55:49)
[PyPy 7.3.12 with GCC 12.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>> import threefive
>>>> threefive.version
'2.4.9'
>>>>
- Release versions are odd.
- Unstable testing versions are even.
Parse SCTE-35 on the command line.
Parse base64
threefive '/DAvAAAAAAAA///wFAVIAACPf+/+c2nALv4AUsz1AAAAAAAKAAhDVUVJAAABNWLbowo='
Parse a hex value
threefive 0xFC302F000000000000FFFFF014054800008F7FEFFE7369C02EFE0052CCF500000000000A0008435545490000013562DBA30A
Parse MPEGTS from stdin
cat video.ts | threefive
Parse MPEGTS video over https
threefive https://so.slo.me/longb.ts
Parse multicast
threefive udp://@235.35.3.5:3535
display realtime program -> pts
a@fu:~$ threefive pts /home/a/msnbc.ts
1-> 3164.442756
1-> 3164.409422
1-> 3164.476089
1-> 3164.476089
1-> 3164.476089
1-> 3164.642756
1-> 3164.576089
display mpegts stream info
a@fu:~$ threefive show https://futzu.com/xaa.ts
Program: 1
Service: Service01
Provider: FFmpeg
Pid: 4096
Pcr Pid: 256
Streams:
Pid: 134[0x86] Type: 0x86 SCTE35 Data
Pid: 256[0x100] Type: 0x1b AVC Video
Pid: 257[0x101] Type: 0xf AAC Audio
Parse SCTE-35 programmatically with a few lines of code.
Mpegts Multicast in three lines of code.
import threefive
strm = threefive.Stream('udp://@239.35.0.35:1234')
strm.decode()
(need an easy multicast server? gumd )
Mpegts over Https in three lines of code.
import threefive
strm = threefive.Stream('https://iodisco.com/ch1/ready.ts')
strm.decode()
</details>
<details><summary>Base64 in five lines of code.</summary>
```python3
>>> from threefive import Cue
>>> stuff = '/DAvAAAAAAAA///wBQb+dGKQoAAZAhdDVUVJSAAAjn+fCAgAAAAALKChijUCAKnMZ1g='
>>> cue=Cue(stuff)
>>> cue.decode()
True
>>> cue.show()
Bytes in five lines of code.
>>> 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 in 4 lines of code.
import threefive
cue = threefive.Cue("0XFC301100000000000000FFFFFF0000004F253396")
cue.decode()
cue.show()
Easy SCTE-35 encoding with threefive.
-
Need SCTE-35 Packet Injection? SuperKabuki, powered by threefive.
-
Helper functions for SCTE35 Cue encoding
Python 3.8.13 (7.3.9+dfsg-5, Oct 30 2022, 09:55:31)
[PyPy 7.3.9 with GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>> import threefive.encode
>>>> help(threefive.encode)
Help on module threefive.encode in threefive:
NAME
threefive.encode - encode.py
DESCRIPTION
threefive.encode has helper functions for Cue encoding.
FUNCTIONS
mk_splice_insert(event_id, pts=None, duration=None, out=False)
mk_cue returns a Cue with a Splice Insert.
The args set the SpliceInsert vars.
splice_event_id = event_id
if pts is None (default):
splice_immediate_flag True
time_specified_flag False
if pts:
splice_immediate_flag False
time_specified_flag True
pts_time pts
If duration is None (default)
duration_flag False
if duration IS set:
out_of_network_indicator True
duration_flag True
break_auto_return True
break_duration duration
pts_time pts
if out is True:
out_of_network_indicator True
if out is False (default):
out_of_network_indicator False
mk_splice_null()
mk_splice_null returns a Cue
with a Splice Null
mk_time_signal(pts=None)
mk_time_signal returns a Cue
with a Time Signal
if pts is None:
time_specified_flag False
if pts IS set:
time_specified_flag True
pts_time pts
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
import sys
import threefive
def do():
arg = sys.argv[1]
with open(arg,'rb',encoding="utf-8") 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.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 -
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'
Parse Custom Splice Descriptors
- Subclass
threefive.descriptors.SpliceDescriptor
- Add
self.private_data
to__init__
- Add a
decode
method - Add it to
threefive.descriptors.descriptor_map
tag:Class112: MDSNDescriptor
import threefive
class MDSNDescriptor(threefive.descriptors.SpliceDescriptor):
"""
MDSNDescriptor
"""
def __init__(self, bites=None):
super().__init__(bites)
self.name = "MDSN Descriptor"
self.private_data=None
def decode(self):
self.private_data="".join(list(self.bites[: self.descriptor_length -4].decode()))
if __name__ == '__main__':
threefive.descriptors.descriptor_map[112]=MDSNDescriptor
cue = threefive.Cue('/DBlAAAAAAAAAP/wBQb+GVJTDABPcAZNRFNOQzUCRUNVRUkAAKTff8MAACky4A8xdXJuOnV1aWQ6QnJlYWstQjAwMjA4NTU2ODlfMDAxMi0wNy0xMC1YMDExMjUxNjEyNDAAAPkSB7E=')
cue.decode()
cue.show()
a@debian:~/clean/scte35-threefive$ pypy3 mdsn.py
{
"info_section": {
"table_id": "0xfc",
"section_syntax_indicator": false,
"private": false,
"sap_type": "0x3",
"sap_details": "No Sap Type",
"section_length": 101,
"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": 5,
"splice_command_type": 6,
"descriptor_loop_length": 79,
"crc": "0xf91207b1"
},
"command": {
"command_length": 5,
"command_type": 6,
"name": "Time Signal",
"time_specified_flag": true,
"pts_time": 4720.284578,
"pts_time_ticks": 424825612
},
"descriptors": [
{
"tag": 112,
"descriptor_length": 6,
"name": "MDSN Descriptor", # <---- Custom Descriptor parsed.
"identifier": "MDSN",
"private_data": "C5"
},
{
"tag": 2,
"descriptor_length": 69,
"name": "Segmentation Descriptor",
"identifier": "CUEI",
"components": [],
"segmentation_event_id": "0xa4df",
"segmentation_event_cancel_indicator": false,
"program_segmentation_flag": true,
"segmentation_duration_flag": true,
"delivery_not_restricted_flag": false,
"web_delivery_allowed_flag": false,
"no_regional_blackout_flag": false,
"archive_allowed_flag": false,
"device_restrictions": "No Restrictions",
"segmentation_duration": 30.0,
"segmentation_duration_ticks": 2700000,
"segmentation_message": "Provider Advertisement Start",
"segmentation_upid_type": 15,
"segmentation_upid_type_name": "URI",
"segmentation_upid_length": 49,
"segmentation_upid": "urn:uuid:Break-B0020855689_0012-07-10-X0112516124",
"segmentation_type_id": 48,
"segment_num": 0,
"segments_expected": 0
}
]
}
Powered by threefive
⚡ 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 | 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 No forms man, just open an issue and tell me what you need.
(It needs to be threefive related or a "What is the meaning of life and stuff?" type of question)
data
this might be wild baseless speculation.
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.29-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 028785d0133ec87452ab786d3607c52673521334648e91629dfcb66c197367e4 |
|
MD5 | b9105bb2ea63470f229a60adb65bb259 |
|
BLAKE2b-256 | 77650860e7c2bf75b4503c82f490b5eba18f4f480ca427ceeb003bb91a331a13 |