Skip to content

CrystalComputerCorp/scte35-threefive

 
 

Repository files navigation

threefive, boom goes the dynamite.

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")

35decode https://futzu.com/xaa.ts, a classy cli tool](https://github.com/futzu/threefive/blob/master/examples/35decode)


Requirements

  • 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.

Install

pip3 install threefive

# for pypy3
pypy3 -m pip install threefive

Versions and Releases

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.


Easy threefive

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 files and Mpegts HTTP/HTTPS Streams

SCTE-35 data can be parsed with just one function call.

SCTE-35 data is printed in JSON format.

Examples:

Base64
import threefive 

stuff = '/DAvAAAAAAAA///wBQb+dGKQoAAZAhdDVUVJSAAAjn+fCAgAAAAALKChijUCAKnMZ1g='
threefive.decode(stuff)
Bytes
import threefive 

payload = b'\xfc0\x11\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\x00\x00\x00O%3\x96'
threefive.decode(payload)
Hex String
import threefive 

stuff = '0XFC301100000000000000FFFFFF0000004F253396'
threefive.decode(stuff)
Hex Literal
import threefive 

threefive.decode(0XFC301100000000000000FFFFFF0000004F253396)
Integer
big_int = 1439737590925997869941740173214217318917816529814
threefive.decode(big_int)
Mpegts File
import threefive 

threefive.decode('/path/to/mpegts')
Mpegts HTTP/HTTPS Streams
import threefive 

threefive.decode('https://futzu.com/xaa.ts')

Mpegts UDP streams

import threefive 

threefive.decode('udp://10.0.0.1:555')

Mpegts Multicast

import threefive 

threefive.decode('udp://@239.35.0.35:1234')

Advanced threefive


Cue Class

  • 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

Stream Class

 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.

Stream.show()

  • 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 Data

Stream.decode(func=show_cue)

import 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()

  • 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()

Stream.decode_program(the_program, func = show_cue)

  • 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)

Stream.decode_proxy(func = show_cue)

  • 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 -

Issues and Bugs and Feature Requests


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.


About

"If you say SCTE-35 one more time, I am going to punch you in the face" ~Monica

Resources

License

Code of conduct

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Python 76.0%
  • Go 23.4%
  • Makefile 0.6%