Giter Site home page Giter Site logo

futzu / scte-35 Goto Github PK

View Code? Open in Web Editor NEW
135.0 15.0 26.0 6.43 MB

threefive is the one you want. threefive is the best SCTE-35 tool available. SCTE-35 Decoder, SCTE-35 Encoder, SCTE-35 Parser.

Home Page: https://slow.golf

License: MIT License

Python 99.33% Makefile 0.67%
scte35 scte ad-splicing scte-35 cuei scte35-parser python3 ad-insertion scte35-2022 mpegts adrianofdoom adbreak scte35-encoder best-scte35-parser threefive adrian scte35-decoder boobs best best-scte-35

scte-35's Introduction

threefive is the highest rated SCTE-35 parser. Ever.

The Reviews are In!

  • If I had some SCTE-35 I would use threefive, absolutely! ~ Lovely, featured dancer the Pink Pony
  • threefive saved my marriage! ~ Dan in Denver
  • threefive rocks my face! ~ Joe from Jersey
  • If you say SCTE-35 one more time, I'm leaving you! ~ My ex-girlfriend


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.


Latest threefive version is 2.4.69

  • threefive 2.4.69
    • fix for parsing ffmpeg "bin data" for SCTE-35
    • return without error for invalid files
  • threefive 2.4.65 introduces Command Line Encoding

Issues and Feature Requests and Asking Me for Help.

threefive is by far the best SCTE-35 software available. Why? It's not just me, it's al the folks that have he;ped along the way. I'm not talking just about writing code. threefive has changed because of feedback from users. This is Open Soure, I put the code and my expertise in the pot, your help find bugs and give feedback.
Wheb you ask for my help, I take you seriously and I resolve most issues in a matter of hours.
When I give you a solution, you have a responsibility to report back what happened.

It's not about me, it's about Open Soure.

I learned everything I know from Open Source projects.

I wrote threefive for two reasons:

  1. Somebody bet me a thousand dollars that I couldn't write a SCTE-35 parser
  2. I was trying to impress a girl
  3. Because I felt like I owed Open Source.

Like my Dad used to tell me,

'Son, Do something, even if it's wrong. '

Documentation

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 -

image


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

FAQ

Q) "How do I capture the output of threefive?"

A) Redirect 2.

a@fu:~$ threefive input.ts 2> output.txt
  • For piping and such, use 2>&1
a@fu:~$ threefive packets  plp0.ts 2>&1 | less

Q) "You aren't on any social media, are you a Chinese secret agent?"

A) No, I am not a Chinese agent, threefive is huge in China though.


Q) "SCTE-35 sucks, can we just hire you to do it for us?"

A) Yes, that's all I've done for the last four years. Just open an issue and we can discuss.




image

scte-35's People

Contributors

futzu avatar vladdoster avatar richard-vd avatar gitfu avatar airlangga-m2amedia avatar deepsource-autofix[bot] avatar jamesfining avatar audiodan avatar deepsourcebot avatar

Stargazers

Amir So avatar  avatar Ana Carolina Castro avatar Miles Kleiber avatar Carlos Emilio Puente avatar Tomasz Oponowicz avatar Warcop avatar Sathish A avatar Sohel Mia avatar DuffXP avatar  avatar Ziggy avatar Gary  avatar Alba Mendez avatar Bubbaprog avatar Lucas Cesar F. de Medeiros avatar Will avatar Philippe Rambourg avatar Benjamin Chen avatar  avatar John Lee avatar  avatar  avatar Alex avatar  avatar Tor Røttum avatar Saken Tsukenofu avatar  avatar Allan Nava avatar Paxx avatar Jonas Hansen avatar Ivan Shakhrai avatar Vivek Gounder avatar Marcel Poelstra Consultancy avatar \\//\\//\\ avatar Mauricio Antunes avatar Ad®ian avatar Marwan Yassin avatar Howei avatar  avatar  avatar Feeman avatar Pozzi avatar Sam Comer avatar tsukumi avatar Bo Bayles avatar  avatar Dmitry avatar  avatar  avatar Guilherme avatar  avatar  avatar Matt Hardiman avatar  avatar Jeel Bhavsar avatar Lars The avatar Karl Böhlmark avatar Bing avatar  avatar Çağdaş Kurultay Kalkan avatar Jonathan Bachmann avatar  avatar Beni Ben zikry avatar Leandro Moreira avatar  avatar Jean Noel Duquesne avatar  avatar  avatar  avatar Paul Gregoire avatar Jan Starzak avatar  avatar Nick Krzemienski avatar Rufael Mekuria avatar ashish avatar Matt Arnold avatar Vytautas Liuolia avatar Alex Jennings avatar Raul Santos avatar  avatar  avatar Nikhil Soman avatar Daniel Keywan Hollister avatar Christopher Olekas avatar  avatar Guillem Cabrera avatar Krystal Mejia avatar Erik Hoffman avatar Mark Ogle avatar  avatar  avatar Loke Dupont avatar Oren Me avatar Michael Nelson avatar Gnaphron avatar Alfred Gutierrez avatar Phani Krishna avatar boby avatar wonjae.h avatar

Watchers

 avatar Bo Bayles avatar David Evans avatar Rev. Scandal avatar Rufael Mekuria avatar  avatar Feeman avatar Varun Vasudeva avatar  avatar Ray Holton avatar  avatar  avatar  avatar  avatar  avatar

scte-35's Issues

IndexError thrown in _decode_components() threefive/commands.py:217

The list is empty upon initialization. A loop then attempts to assign indexes of the empty list

Thrown from threefive/commands.py:217

    def _decode_components(self, bitbin):
        """
        SpliceInsert._decode_components loops
        over SpliceInsert.components,
        and is called from SpliceInsert.decode()
        """
        self.component_count = bitbin.as_int(8)
        self.components = []
        for i in range(0, self.component_count):
>           self.components[i] = bitbin.as_int(8)
E           IndexError: list assignment index out of range

The request that triggered the error

% python3               
Python 3.11.2 (main, Feb 16 2023, 02:55:59) [Clang 14.0.0 (clang-1400.0.29.202)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from threefive import SpliceInsert
>>> bites = b'\xfc0%\x00\x00\x00\x03(\x98\x00\xff\xf0\x14\x05d-\xe5Y\x7f\xef\xfe\x00\x1a\xea\xa0\xfe\x00Re\xc0\x00\x01\x01\x01\x00\x00\x88\x8c\xc9\xed'
>>> SpliceInsert(bites).decode()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/homebrew/lib/python3.11/site-packages/threefive/commands.py", line 190, in decode
    self._decode_components(bitbin)
  File "/opt/homebrew/lib/python3.11/site-packages/threefive/commands.py", line 217, in _decode_components
    self.components[i] = bitbin.as_int(8)
    ~~~~~~~~~~~~~~~^^^
IndexError: list assignment index out of range

A possible fix

    def _decode_components(self, bitbin):
        """ 
        SpliceInsert._decode_components loops
        over SpliceInsert.components,
        and is called from SpliceInsert.decode()
        """
        self.component_count = bitbin.as_int(8)
        self.components = [bitbin.as_int(8) for _ in range(0, self.component_count)]

A similar error is expected to be thrown at threefive/commands.py:339 as well

Issue with decode() and decode_proxy() functions

Using decode() or decode_proxy(), both functions are giving below error:

File "proxy_d35.py", line 6, in
sp.decode_proxy()
File "/usr/local/lib/python3.6/dist-packages/threefive/stream.py", line 127, in decode_proxy
cue = self._parser(pkt)
File "/usr/local/lib/python3.6/dist-packages/threefive/stream.py", line 186, in _parser
return self._parse_scte35(pkt, pid)
File "/usr/local/lib/python3.6/dist-packages/threefive/stream.py", line 219, in _parse_scte35
packet_data = self._mk_packet_data(pid)
File "/usr/local/lib/python3.6/dist-packages/threefive/stream.py", line 147, in _mk_packet_data
packet_data["pts"] = round(self._prog_pts[prgm], 6)

Is than an issue in release or something I might be doing wrong?

Consider making crcmod an optional dependency

Commit 1feffa6 allowed users without crcmod installed to use the bits ofCue functionality that don't require it.

However, setup.py brings in crcmod unconditionally. This means that users who install threefive wind up with crcmod.

My request is to make crcmod an optional dependency, i.e. to put it in an extras_require. This would allow users to control whether they get it or not.

In that world pip install threefive wouldn't bring in crcmod, but something like pip install threefive[decoding] would.

The same request could be made re: pyaes, but it's less pressing in that case because it is a pure Python module that doesn't need to be compiled for exotic architectures.

Thanks for this excellent library!

cmd.py in latest PyPI release is not syntaxically correct

Hello,

The file cmd.py in the latest release in PyPI breaks RPM builds, because it tries to compile to python bytecode all the files and cmd.py is not syntaxically correct:

*** Error compiling '/builds/myproject/.venv/lib/python3.6/site-packages/threefive/cmd.py'...
  File "/cmd.py", line 1
    /*
    ^
SyntaxError: invalid syntax

FFmpeg SCTE-35 Stream Type

futzu wrote.. "I don't know how to stop ffmpeg from changing the SCTE35 stream type to 0x6."

FYI - I did some experiments with this and it seems to be a limitation of FFmpeg's TS muxer.

If I use the NUT container (FFmpeg's proprietary container format), the -tag:d can be used to set the type. In theory, this tag should also be capable of setting the type via a fourcc value, but that does not seem to be supported for data streams. Hex or decimal work with a NUT container though.

In this case, the stream description was changed from scte_35 to none.

$ ffmpeg -hide_banner -i "https://futzu.com/xaa.ts" -map 0:v:0 -codec:v copy -streamid 0:0x101 -map 0:a:0 -codec:a copy -streamid 1:0x102 -map 0:d:0 -codec:d:0 copy -streamid 2:0x103 -tag:d:0 "0x86" -f nut OUTFILE.nut -y

# or

$ ffmpeg -hide_banner -i "https://futzu.com/xaa.ts" -map 0:v:0 -codec:v copy -streamid 0:0x101 -map 0:a:0 -codec:a copy -streamid 1:0x102 -map 0:d:0 -codec:d:0 copy -streamid 2:0x103 -tag:d:0 "134" -f nut OUTFILE.nut -y

$ ffprobe OUTFILE.nut
  Stream #0:0: Video: h264 (Main) (H264 / 0x34363248), yuv420p(tv, bt470bg), 544x576 [SAR 64:33 DAR 544:297], 50 fps, 50 tbr, 90k tbn, 50 tbc
  Stream #0:1(eng): Audio: mp2 (P[0][0][0] / 0x0050), 48000 Hz, stereo, s16p, 128 kb/s
  Stream #0:2: Data: none ([134][0][0][0] / 0x0086)

If the same command is used with an MPEG-TS container, the FFmpeg muxer overwrites the type to be 0x6. At runtime / console output, it claims it is setting the 0x86, but when probed, the file output is 0x6.

In this case, the stream type was changed from scte_35 to bin_data.

$ ffmpeg -hide_banner -i "https://futzu.com/xaa.ts" -map 0:v:0 -codec:v copy -streamid 0:0x101 -map 0:a:0 -codec:a copy -streamid 1:0x102 -map 0:d:0 -codec:d:0 copy -streamid 2:0x103 -tag:d:0 "0x86" -f mpegts OUTFILE.ts -y

$ ffprobe OUTFILE.ts
  Stream #0:0[0x101]: Video: h264 (Main) ([27][0][0][0] / 0x001B), yuv420p(tv, bt470bg, top first), 544x576 [SAR 64:33 DAR 544:297], 25 fps, 50 tbr, 90k tbn, 50 tbc
  Stream #0:1[0x102](eng): Audio: mp2 ([3][0][0][0] / 0x0003), 48000 Hz, stereo, fltp, 128 kb/s
  Stream #0:2[0x103]: Data: bin_data ([6][0][0][0] / 0x0006)

This is not an issue with threefive, it is purely informational and can be closed. I recognize that threefive successfully works around FFmpeg's limitation. Raising the ticket was just a means of sharing knowledge and narrowing down where FFmpeg was struggling. Neither does the ticket solve the issue because the ultimate goal is setting it in a TS container, not a NUT container. Feel free to close this issue.

: W0703: Catching too general exception Exception (broad-except)

Vlad,
do you have time to sort this out?

I can post the script I use to test all the cases.

************* Module threefive.decode
threefive/decode.py:25:11: W0703: Catching too general exception Exception (broad-except)
threefive/decode.py:32:15: W0703: Catching too general exception Exception (broad-except)
threefive/decode.py:49:11: W0703: Catching too general exception Exception (broad-except)
threefive/decode.py:58:11: W0703: Catching too general exception Exception (broad-except)
threefive/decode.py:68:11: W0703: Catching too general exception Exception (broad-except)

@richard

Firstly, please don't hit me 4 pull requests at once,
I have spent hours on Hex padding alone.
Lets do them one at a time please.

I can live with padding, but this is not so good,

 nibbles = (num_bits + 3) >> 2

Since we already have k, instead of num_bits use k.bit_length()

Is there any other way we can do it besides +3 >>2 ?

The code does not have to be super short.

You also need to verify your changes work with NBin for encoding,
that may need updating, I don't know.

Lets resolve this , and then we can get to UUID, we have to have an encode method.
I forgot about that.

Does anyone speak DASH?

DVB.org put threefive in DVB-DASH Spec as a SCTE-35 parser.
I feel like I need to add direct DASH support to threefive or build something like x9k3 for DASH.
If you do speak DASH, let's collaborate. You can write code, or just advise me.
If you know DASH, say something. That's the point of this issue.

Adrian

Tried editing a splice insert from example but getting different result

I am trying to edit a break_duration from given example in: https://github.com/futzu/scte35-threefive/blob/master/Encoding.md#edit-a-splice-insert-command-in-a--scte35-cue.

I have used the same commands shared in example,

Python 3.10.6 (main, Nov 14 2022, 16:10:14) [GCC 11.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import threefive
>>> Base64 = "/DAvAAAAAAAA///wFAVIAACPf+/+c2nALv4AUsz1AAAAAAAKAAhDVUVJAAABNWLbowo="
>>> cue = threefive.Cue(Base64)
>>> cue.decode()
True
>>> cue.command
{'command_length': 20, 'command_type': 5, 'name': 'Splice Insert', 'time_specified_flag': True, 'pts_time': 21514.559089, 'pts_time_ticks': 1936310318, 'break_auto_return': True, 'break_duration': 60.293567, 'break_duration_ticks': 5426421, 'splice_event_id': 1207959695, 'splice_event_cancel_indicator': False, 'out_of_network_indicator': True, 'program_splice_flag': True, 'duration_flag': True, 'splice_immediate_flag': False, 'component_count': None, 'components': None, 'unique_program_id': 0, 'avail_num': 0, 'avail_expected': 0}
>>> cue.command.break_duration= 90.0
>>> cue.encode()
'/DAvAAAAAAAA///wFAVIAACPf+/+c2nALv4AUsz1AAAAAAAKAAhDVUVJAAABNWLbowo='

But instead of getting expected result as in example, I am getting the previous/original encoded string with break_duration:60.293567.

Could you please suggest if I am missing something?

Thank You

unable to re-encode the following base64 /DA8AAAAAAAAAP/wBQb/ZoaJUwAmAiRDVUVJBPpHwH/9AABSY2IMEERJU0NTTURDMDc3MzAwTEg0AQESS6TU

I'm trying to re-encode a signal after modifying pts value without touching any of the descriptors
I have no issues with several segmentation_type_id 48 that I've tried but for some reason I can't re-encode the following one (segmentation type id 52) - /DA8AAAAAAAAAP/wBQb/ZoaJUwAmAiRDVUVJBPpHwH/9AABSY2IMEERJU0NTTURDMDc3MzAwTEg0AQESS6TU

Traceback (most recent call last):
  File "/Users/lshaked/scte35/./scte35processor.py", line 24, in <module>
    sig_binary_data = new_cue.encode()
  File "/Users/lshaked/scte35/lib/python3.9/site-packages/threefive/cue.py", line 210, in encode
    dscptr_bites = self._unloop_descriptors()
  File "/Users/lshaked/scte35/lib/python3.9/site-packages/threefive/cue.py", line 262, in _unloop_descriptors
    dbite_chunks = [dsptr.encode() for dsptr in self.descriptors]
  File "/Users/lshaked/scte35/lib/python3.9/site-packages/threefive/cue.py", line 262, in <listcomp>
    dbite_chunks = [dsptr.encode() for dsptr in self.descriptors]
  File "/Users/lshaked/scte35/lib/python3.9/site-packages/threefive/descriptors.py", line 335, in encode
    self._encode_segmentation(nbin)
  File "/Users/lshaked/scte35/lib/python3.9/site-packages/threefive/descriptors.py", line 381, in _encode_segmentation
    self._encode_segments(nbin)
  File "/Users/lshaked/scte35/lib/python3.9/site-packages/threefive/descriptors.py", line 394, in _encode_segments
    self._chk_var(int, nbin.add_int, "sub_segment_num", 8)  # 1 byte
  File "/Users/lshaked/scte35/lib/python3.9/site-packages/threefive/base.py", line 104, in _chk_var
    raise ValueError(err_mesg)
ValueError: sub_segment_num is not set, it should be type <class 'int'>

here is the new_cue before encode:

{'bites': None, 'info_section': {'table_id': '0xfc', 'section_syntax_indicator': False, 'private': False, 'sap_type': '0x3', 'sap_details': 'No Sap Type', 'section_length': 60, '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': 38, 'crc': '0x124ba4d4'}, 'command': {'command_length': 5, 'command_type': 6, 'name': 'Time Signal', 'bites': None, 'time_specified_flag': True, 'pts_time': 66834.03677777777, 'pts_time_ticks': 6015063310}, 'descriptors': [{'tag': 2, 'descriptor_length': 36, 'name': 'Segmentation Descriptor', 'identifier': 'CUEI', 'bites': None, 'provider_avail_id': None, 'components': [], 'segmentation_event_id': '0x4fa47c0', 'segmentation_event_cancel_indicator': False, 'component_count': None, 'program_segmentation_flag': True, 'segmentation_duration_flag': True, 'delivery_not_restricted_flag': True, 'web_delivery_allowed_flag': None, 'no_regional_blackout_flag': None, 'archive_allowed_flag': None, 'device_restrictions': None, 'segmentation_duration': 59.993267, 'segmentation_duration_ticks': 5399394, 'segmentation_message': 'Provider Placement Opportunity Start', 'segmentation_upid_type': 12, 'segmentation_upid_type_name': 'MPU', 'segmentation_upid_length': 16, 'segmentation_upid': {'format_identifier': 1145656131, 'private_data': '0x534d44433037373330304c48'}, 'segmentation_type_id': 52, 'segment_num': 1, 'segments_expected': 1, 'sub_segment_num': None, 'sub_segments_expected': None}], 'packet_data': None}

code is:

import threefive
from threefive import Cue

sig_binary_data="/DA8AAAAAAAAAP/wBQb/ZoaJUwAmAiRDVUVJBPpHwH/9AABSY2IMEERJU0NTTURDMDc3MzAwTEg0AQESS6TU" # doesn't work
#sig_binary_data="/DA8AAAAAAAAAP/wBQb+TiMTvgAmAiRDVUVJBRjPQX/9AAELyNwMEERJU0MxMDAzMDI0Nl85OTkwAQEAQHzt" # works PA start
#sig_binary_data="/DA8AAAAAAAAAP/wBQb/Xz380gAmAiRDVUVJBPpG2X/9AADN+HUMEERJU0MxMDAzNTk2N185OTkwAQE1K48T" # works
scte_35_cue = threefive.Cue(sig_binary_data)
scte_35_cue.decode()
print(scte_35_cue)
print(scte_35_cue.descriptors[0].sub_segment_num,"\n")
scte_35_dict = scte_35_cue.get()
for x in scte_35_dict['descriptors'][0].keys():
    print("key:",x,"value:",scte_35_dict['descriptors'][0][x])
print(scte_35_dict['descriptors'][0].keys())
print("signal pts_time",scte_35_dict['command']['pts_time_ticks'])
recorded_pts_time = scte_35_dict['command']['pts_time_ticks'] + 3003
print("new pts",str(recorded_pts_time))
#scte_35_dict['command']['pts_time_ticks'] = recorded_pts_time
#scte_35_dict['command']['pts_time'] = float(recorded_pts_time/90000)
new_cue = threefive.Cue()
new_cue.load(scte_35_dict)
print(new_cue)
sig_binary_data = new_cue.encode()
print(sig_binary_data)

using latest version - 2.3.81

The SCTE35 "section_length" value is always 3 bytes less than the actual size

I'm learning SCTE35 staff and I can't figure out one thing. Why is the length of "section_length" always less than 3 bytes from the actual size?
Example:

>>> test_raw = "/DAlAAAAAAAAAP/wFAUAAAZ5f+/+AAAAAH4Ae5igAAAAAAAA5nk27g=="
>>> test_cue = Cue(test_raw)
>>> test_cue.decode()
>>> test_cue.show()
"section_length": 37,

But if I will check len(test_cue.bites) will see 40:

>>> len(test_cue.bites)
40

I've already checked the documentation but did not find answer:

Probably some "header" that is not included in the "section_length". From the source of the lib i see that "section_length" calculation like this - # 11 bytes for info section + command + 2 descriptor loop length + descriptor loop + 4 for crc, but I`m still confused.

Сould someone please explain why we have this 3 byte difference in "section_length" and the actual size ?

Sorry, the question not related to the project

UPID parsing with strange output

I'm using threefive in a tool that monitors a live HLS stream.
The stream follows the "Addressable TV" spec (https://www.snptv.org/wp-content/uploads/2020/08/SNPTV-AFMM-Addressable-TV-UK-version-2.0.6.1.pdf)

It contains SCTE35 payloads with a 0x02 descriptor "CallAdServer", which contains a 16-byte upid of type "MPU" (Managed Private UPID - 0x0C), which should have the following structure:

• Bytes from 1 to 4: ASCII code for ‘ADFR’ = 0x41444652
• Byte #5: version number: by default 1 = 0x01
• Bytes 6 to 7: TV channel ID - CNI: example for TF1 = 0x33F1
• Bytes 8 to 11 (4 bytes): YYYYMMDD = 0x01341403 (20190211 in decimal)
• Bytes 12 and 13: Ad break code: example = 0x0462 (break 1122)
• Bytes 14 to 16: duration of the break in ms = 0x01C070 (duration: 114800 ms, meaning: 1minute,
54secondes and 20 frames).

Here is an example: 0xFC305E00014ECF5F9800FFF00506FE156DE4F0004802144355454900065E087FFF00001B77400000300710021F4355454900065EFF7FBF0C10414446520133F10134B04F065E060220020000020F4355454900065E077FBF00003106106F4BCB35

I'm a bit puzzled about the output of threefive for that UPID:

{
            "tag": 2,
            "descriptor_length": 31,
            "name": "Segmentation Descriptor",
            "identifier": "CUEI",
            "components": [],
            "segmentation_event_id": "0x65eff",
            "segmentation_event_cancel_indicator": false,
            "program_segmentation_flag": true,
            "segmentation_duration_flag": false,
            "delivery_not_restricted_flag": true,
            "segmentation_upid_type": 12,
            "segmentation_upid_type_name": "MPU",
            "segmentation_upid_length": 16,
            "segmentation_upid": {
                "format_identifier": 1094993490,
                "private_data": "0x133f10134b04f065e060220"
            },
            "segmentation_type_id": 2
        },

The private_data element does not seem to be a valid hex string.
Or at least Python's bytes.fromhex() refuses it.

By the way, would you consider adding the "CallAdServer" segmentation type ID in your table22?
In my code I simply add it in the following way:
threefive.segmentation.table22[2] = "Call Ad Server"

SCTE-35 Preroll

I think your side of the conversation in the tsduck thread was deleted. Let's pick it up here.

It will take me some time to get up to speed on installing and using threefive. In the meantime, where do I grab the latest version that you added preroll to?

edit: I see it, it's the 'master' code thread (terrible name, github... come on)

'hasp.py' unable to parse #EXT-X-CUE-OUT, #EXT-X-CUE-OUT-CONT, #EXT-X-CUE-IN

Hi @futzu

Following up with another issue, hasp.py is unable to parse the SCTE-35 HLS cues in my two m3u8 playlists:

  • #EXT-X-CUE-OUT
  • #EXT-X-CUE-OUT-CONT
  • #EXT-X-CUE-IN

Playlist 1 has the following syntax:

#EXT-X-CUE-OUT:90.023267
#EXT-X-CUE-OUT-CONT:6.006/90.023267
(..... Removed on purpose ....)
#EXT-X-CUE-OUT-CONT:78.078/90.023267
#EXTINF:6.006,
#EXT-X-CUE-OUT-CONT:84.084/90.023267
#EXTINF:5.939267,
#EXT-X-CUE-IN
#EXTINF:4.004,
#EXTINF:6.006,

While Playlist 2 has a different structure:

#EXTINF:4.0,
sftv_480p/media-ucvpbplas_228029.ts
#EXTINF:1.12,
sftv_480p/media-ucvpbplas_228030.ts
#EXT-OATCLS-SCTE35:c3BsaWNlLTY3MTE0OTQw
#EXT-X-ASSET:CAID=splice-67114940
#EXT-X-CUE-OUT:30.00
#EXT-X-DATERANGE:ID="splice-67114940",START-DATE="2022-02-22T04:33:38.99782700Z",PLANNED-DURATION=30.00,SCTE35-OUT=0x000000000000000000000000000000323032322D30322D32325430343A33333A33382E39393738323730305A
#EXTINF:7.0,
sftv_480p/media-ucvpbplas_228031.ts
#EXT-X-CUE-OUT-CONT:ElapsedTime=7.00,Duration=30.00,SCTE35=c3BsaWNlLTY3MTE0OTQw
#EXTINF:4.0,
sftv_480p/media-ucvpbplas_228032.ts
#EXT-X-CUE-OUT-CONT:ElapsedTime=11.00,Duration=30.00,SCTE35=c3BsaWNlLTY3MTE0OTQw
#EXTINF:4.0,
sftv_480p/media-ucvpbplas_228033.ts
#EXT-X-CUE-OUT-CONT:ElapsedTime=15.00,Duration=30.00,SCTE35=c3BsaWNlLTY3MTE0OTQw
#EXTINF:4.0,
sftv_480p/media-ucvpbplas_228034.ts
#EXT-X-CUE-OUT-CONT:ElapsedTime=19.00,Duration=30.00,SCTE35=c3BsaWNlLTY3MTE0OTQw
#EXTINF:4.0,
sftv_480p/media-ucvpbplas_228035.ts
#EXT-X-CUE-OUT-CONT:ElapsedTime=23.00,Duration=30.00,SCTE35=c3BsaWNlLTY3MTE0OTQw
#EXTINF:4.0,
sftv_480p/media-ucvpbplas_228036.ts
#EXTINF:4.0,
sftv_480p/media-ucvpbplas_228036.ts
#EXT-X-CUE-OUT-CONT:ElapsedTime=27.00,Duration=30.00,SCTE35=c3BsaWNlLTY3MTE0OTQw
#EXTINF:3.0,
sftv_480p/media-ucvpbplas_228037.ts
#EXT-X-CUE-IN
#EXT-X-DATERANGE:ID="splice-67114941",DURATION=0.00,SCTE35-IN=0x000000000000000000000000000000323032322D30322D32325430343A33343A30382E39393738323730305A
#EXTINF:5.0,
sftv_480p/media-ucvpbplas_228038.ts

None of them can be parsed with hasp.py, could you have a look? I can provide you the m3u8 files.

threefive v2.2.91 bug in Stream class

Version 2.2.91 has a bug when parsing MPEGTS with the Stream class.
Stream._parse calls Stream.,_split_by_idx too often.

  ncalls    tottime  percall  cumtime  percall filename:lineno(function)
  377960    0.129    0.000    0.129    0.000    stream.py:185(_split_by_idx)

fixed in version 2.2.93

 ncalls     tottime  percall  cumtime  percall filename:lineno(function)
    35      0.000    0.000    0.000    0.000   stream.py:169(_split_by_idx)

Crash when decoding private descriptors from PrivateCommand

I'm experiencing an issue when reading SCTE-35 cues from an MPEG-TS stream using this library. The stream in question has periodic PrivateCommand SCTE-35 command types, as shown below in the pcrextract output.

* pcrextract: PID: 0x0033 (51), SCTE 35 command PrivateCommand
* pcrextract: PID: 0x0033 (51), SCTE 35 command PrivateCommand
* pcrextract: PID: 0x0033 (51), SCTE 35 command PrivateCommand
* pcrextract: PID: 0x0033 (51), SCTE 35 command PrivateCommand
* pcrextract: PID: 0x0033 (51), SCTE 35 command PrivateCommand
* pcrextract: PID: 0x0033 (51), SCTE 35 command SpliceInsert out, exec at PTS 0x1089E9D7D

Observed behavior

Everytime a PrivateCommand appears in the PID, threefive crashes.

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/cmcdaniels/.pyenv/versions/3.12.1/lib/python3.12/site-packages/threefive/stream.py", line 234, in decode
    cue = self._parse(pkt)
          ^^^^^^^^^^^^^^^^
  File "/Users/cmcdaniels/.pyenv/versions/3.12.1/lib/python3.12/site-packages/threefive/stream.py", line 486, in _parse
    cue = self._parse_scte35(pkt, pid)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/cmcdaniels/.pyenv/versions/3.12.1/lib/python3.12/site-packages/threefive/stream.py", line 534, in _parse_scte35
    cue = self._parse_cue(pay, pid)
          ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/cmcdaniels/.pyenv/versions/3.12.1/lib/python3.12/site-packages/threefive/stream.py", line 511, in _parse_cue
    if cue.decode():
       ^^^^^^^^^^^^
  File "/Users/cmcdaniels/.pyenv/versions/3.12.1/lib/python3.12/site-packages/threefive/cue.py", line 72, in decode
    bites = self._mk_descriptors(bites)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/cmcdaniels/.pyenv/versions/3.12.1/lib/python3.12/site-packages/threefive/cue.py", line 165, in _mk_descriptors
    self._descriptor_loop(bites[:dll])
  File "/Users/cmcdaniels/.pyenv/versions/3.12.1/lib/python3.12/site-packages/threefive/cue.py", line 84, in _descriptor_loop
    spliced = splice_descriptor(loop_bites)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/cmcdaniels/.pyenv/versions/3.12.1/lib/python3.12/site-packages/threefive/descriptors.py", line 403, in splice_descriptor
    spliced = ( SpliceDescriptor(bites),descriptor_map[tag](bites))[tag in descriptor_map]
                                        ~~~~~~~~~~~~~~^^^^^
KeyError: 68

After digging through the code, it seems that the decode logic is expecting a defined value within the descriptor map for the descriptor type, but since my stream has private descriptors tags an exception occurs on the lookup. The tag is able to be any u32 so it would seem there needs to be support for private descriptors added to this library.

crash in some SCTE-35 packet in v.2.2.37

I haven't yet found the the exact packet data leading to this crash. Here is the traceback:

  File "./mcastc.py", line 74, in <module>
    read_stream(mcast_sock)
  File "./mcastc.py", line 50, in read_stream
    ts.decode()
  File "./mcastc.py", line 35, in decode
    cue = self._parser(pkt)
  File "/usr/local/lib/python3.6/site-packages/threefive/stream.py", line 173, in _parser
    return self._parse_scte35(pkt, pid)
  File "/usr/local/lib/python3.6/site-packages/threefive/stream.py", line 208, in _parse_scte35
    return Cue(pkt, packet_data)
  File "/usr/local/lib/python3.6/site-packages/threefive/cue.py", line 59, in __init__
    self._parse(payload)
  File "/usr/local/lib/python3.6/site-packages/threefive/cue.py", line 64, in _parse
    payload = self._mk_descriptors(payload)
  File "/usr/local/lib/python3.6/site-packages/threefive/cue.py", line 92, in _mk_descriptors
    self._descriptorloop(payload, dll)
  File "/usr/local/lib/python3.6/site-packages/threefive/cue.py", line 103, in _descriptorloop
    spliced = self._set_splice_descriptor(payload)
  File "/usr/local/lib/python3.6/site-packages/threefive/cue.py", line 181, in _set_splice_descriptor
    spliced.decode(bitbin)
  File "/usr/local/lib/python3.6/site-packages/threefive/segmentation.py", line 102, in decode
    self._set_segmentation(bitbin) 
  File "/usr/local/lib/python3.6/site-packages/threefive/segmentation.py", line 128, in _set_segmentation
    self.segmentation_upid_type = bitbin.asint(8)  # 1 byte
  File "/usr/local/lib/python3.6/site-packages/bitn.py", line 20, in asint
    return (self.bits >> (self.idx)) & ~(~0 << num_bits)
ValueError: negative shift count   

hasp.py error in decoding hls manifest

the issue I am experiencing when testing hasp.py with my project is that when I try to it to decode scte35 messages I am getting the following error:
======error log start
Traceback (most recent call last):
File "/home/dhuang/scte35-threefive/examples/hls/hasp.py", line 177, in
HASP(arg).decode()
File "/home/dhuang/scte35-threefive/examples/hls/hasp.py", line 150, in decode
hold = stanza.decode()
File "/home/dhuang/scte35-threefive/examples/hls/hasp.py", line 96, in decode
self._aes_start(line)
File "/home/dhuang/scte35-threefive/examples/hls/hasp.py", line 38, in _aes_start
self._aes_decrypt()
File "/home/dhuang/scte35-threefive/examples/hls/hasp.py", line 49, in _aes_decrypt
with urllib.request.urlopen(self.segment) as infile:
File "/usr/lib/python3.8/urllib/request.py", line 222, in urlopen
return opener.open(url, data, timeout)
File "/usr/lib/python3.8/urllib/request.py", line 531, in open
response = meth(req, response)
File "/usr/lib/python3.8/urllib/request.py", line 640, in http_response
response = self.parent.error(
File "/usr/lib/python3.8/urllib/request.py", line 569, in error
return self._call_chain(*args)
File "/usr/lib/python3.8/urllib/request.py", line 502, in _call_chain
result = func(*args)
File "/usr/lib/python3.8/urllib/request.py", line 649, in http_error_default
raise HTTPError(req.full_url, code, msg, hdrs, fp)
urllib.error.HTTPError: HTTP Error 404: Not Found
======error log end
the url & command I am using as a test:
python3 $HOME/scte35-threefive/examples/hls/hasp.py https://9now-livestreams.akamaized.net/hls/live/2007330/ch9-syd/master1.m3u8

interestingly, when I download the manifest(m3u8) manually and using hlsparse.py and it decoded without any issue.
I am hoping someone can assist me in pointing me in the right direction.
Sorry for being new to this and please let me know if you require additional info.

Thanks,
ozrocozroc

TUI for Encoding SCTE35 String Values

I don't care what it looks like, that's up to you.
I'll explain functionality, but keep your code loose,
we have to figure it out as we go.

Vlad, ask questions as they come up.

We can do it in small chunks.
Let's start with the Splice Command.

On the TUI, we need to select a splice command.

  • BandwidthReservation
  • PrivateCommand
  • SpliceInsert
  • SpliceNull
  • TimeSignal

Selecting a command should trigger the fields
for that command to appear.

Fields are determined by which command is selected
get them by creating an instance of the command

These fields need to be editable.

>>>> from threefive.commands import SpliceInsert
>>>> si = SpliceInsert()
>>>> vars(si)
{'command_length': 0, 'bites': None, 'name': 'Splice Insert', 'time_specified_flag': None, 
'pts_time': None, 'break_auto_return': None, 'break_duration': None, 'splice_event_id': None, 
'splice_event_cancel_indicator': None, 'out_of_network_indicator': None, 'program_splice_flag': None, 
'duration_flag': None, 'splice_immediate_flag': None, 'components': None, 'component_count': None,
'unique_program_id': None, 'avail_num': None, 'avail_expected': None}

>>>> from threefive.commands import TimeSignal
>>>> ts = TimeSignal()
>>>> vars(ts)
{'command_length': 0, 'bites': None, 'name': 'Time Signal', 'time_specified_flag': None, 'pts_time': None}

Use whatever TUI you like, but have you seen this?
npyscreen

Handle unknown chars in segmentation_upid

Input /DBKAAAAAAAAAP/wBQb+YtC8/AA0AiZDVUVJAAAD6X/CAAD3W3ACEmJibG5kcHBobkQCAsGDpQIAAAAAAAEKQ1VFSRSAIyowMljRk9c=

Actual behaviour: UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc1 in position 12: invalid start byte at https://github.com/futzu/scte35-threefive/blob/f41c8a614abc1c866c5ddb48caf54368c235145a/threefive/bitn.py#L55

Expected behaviour:

  • Either, having that exception handled, setting segmentation_upid to a default string
  • Or, allowing user to provide encoding charset

decode local ts file fails...

getting ''str' object has no attribute 'read'' from threefive version 2.4.9

when running the posted example code below.

Can you suggest any updates or changes?

% cat z.py
"""
Example using Stream.decode() with a custom function
to return a list of cues from a video

Usage:

pypy3 cue_list.py video.ts

"""
import sys
import threefive
from threefive import Stream, Cue

# A list to hold cues 
CUES=[]

def cue_func(cue):
    """
    threefive.Stream.decode cqn be passed in a
    custom function that accepts a Cue as the only arg.
    this is an example of a custom function.

    """
    CUES.append(cue)
    cue.show()


def do():
    """
    do collects a list of Cues  from a Stream
    """
    arg = sys.argv[1]
    strm = Stream(arg)
    strm.decode(func=cue_func) # cue_func is the custom function from above


if __name__ == "__main__":
    do()
    [print(cue.command.name,cue.encode()) for cue in CUES]

the output in full reads:
     python3 z.py /path/index_1_00008.ts

Traceback (most recent call last):
  File "/Users/robclem/scripts/z.py", line 40, in <module>
    do()
  File "/Users/robclem/scripts/z.py", line 36, in do
    strm.decode(func=cue_func) # cue_func is the custom function from above
    ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/robclem/scripts/threefive/stream.py", line 79, in decode
    if not self._find_start():
           ^^^^^^^^^^^^^^^^^^
  File "/Users/robclem/scripts/threefive/stream.py", line 65, in _find_start
    one = self._tsdata.read(1)
          ^^^^^^^^^^^^^^^^^
AttributeError: 'str' object has no attribute 'read'

Decode / encode round trip produces unexpected results

Here's something I ran into:

from threefive import Cue

orig = '/DAlAAAAAyiYAP/wFAVNqW2Jf+//WP/YQ/4AUnrYAAEBAQAAGk9JoA=='
orig_cue = Cue(orig)
orig_cue.decode()

new_cue = Cue(orig_cue.encode())
new_cue.decode()

print(orig_cue.get()['info_section']['pts_adjustment_ticks'], new_cue.get()['info_section']['pts_adjustment_ticks'])

Different results for the two values: 207000 206999


I think the issue is probably in the encode process:

It looks like pts_adjustment_ticks is still saved as an integer, so perhaps it could be encoded again directly instead of going through the computed value?

Issues with example scripts

Firstly, super many thanks for these tools, amazing work!

Similar to Issue26, I really want the preroll, but most of the example scripts I get various errors for.

cue_list_too.py seems the only one I can get working

$ pypy3 cue_list_too.py cam-scte-out.ts
{
    "info_section": {
        "table_id": "0xfc",
...truncated

But not cue_list.py

$ pypy3 cue_list.py cam-scte-out.ts
Traceback (most recent call last):
  File "cue_list.py", line 34, in <module>
    for cue in do():
  File "cue_list.py", line 25, in do
    cue = strm.decode_next()
  File "/home/me/.local/lib/pypy3.6/site-packages/threefive/stream.py", line 206, in decode_next
    cue = self.decode(func=False)
  File "/home/me/.local/lib/pypy3.6/site-packages/threefive/stream.py", line 170, in decode
    if not self._find_start():
  File "/home/me/.local/lib/pypy3.6/site-packages/threefive/stream.py", line 154, in _find_start
    one = self._tsdata.read(1)
ValueError: read of closed file

2 quick points

With REF to issue 68

  1. Apologies if I've misunderstood, but just to confirm, are you saying that preroll.py and cue_list.py are known as not to work, or should they be working?
  2. I had to use the following with cue2vtt.py
$ python3 cue2vtt.py cam-scte-out.ts /dev/stdin vlc cam-scte-out.ts -sub -
WEBVTT


 00:00:2.2 -->  00:00:7.2 align:left
command_length  :  20
command_type  :  5
name  :  Splice Insert
time_specified_flag  :  True
pts_time  :  5.0
pts_time_ticks  :  450000
break_auto_return  :  True
break_duration  :  10.0
break_duration_ticks  :  900000
splice_event_id  :  256
splice_event_cancel_indicator  :  False
out_of_network_indicator  :  True
program_splice_flag  :  True
duration_flag  :  True
splice_immediate_flag  :  False
unique_program_id  :  0
avail_num  :  0
avail_expected  :  1

 00:00:3.033 -->  00:00:8.033 align:left
command_length  :  20
command_type  :  5
name  :  Splice Insert
time_specified_flag  :  True
pts_time  :  5.0
pts_time_ticks  :  450000
break_auto_return  :  True
break_duration  :  10.0
break_duration_ticks  :  900000
splice_event_id  :  256
splice_event_cancel_indicator  :  False
out_of_network_indicator  :  True
program_splice_flag  :  True
duration_flag  :  True
splice_immediate_flag  :  False
unique_program_id  :  0
avail_num  :  0
avail_expected  :  1

Having latest.py file breaks rpm build

Hello,

The file latest.py in master branch breaks RPM builds, because it tries to compile to python bytecode all the files and latest.py is not syntaxically correct:

+ /usr/lib/rpm/brp-python-bytecompile /usr/bin/python3 1
*** Error compiling '/builds/myproject/.venv/lib/python3.6/site-packages/threefive/latest.py'...
  File "/latest.py", line 1
    2.3.75
         ^
SyntaxError: invalid syntax

Infinite loop in stream.py::_unpad()

Hi Adrian,

I wanted to bring to your attention a loop that does not terminate.

The call stack is

_unpad, stream.py:445  <- recursive function call
_unpad_afc, stream.py:438
_parse_payload, stream.py:454
_parse_pts, stream:407
_parse, stream.py:493
decode_next, stream: 253

The cause, in my use case, is initializing a Stream() object with a .mp4 segment instead of a .ts

How to reproduce:

% threefive http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4 

# PAT changed @ 94287.385522


# PAT changed @ 94287.385522


# PAT changed @ 94287.385522


# PAT changed @ 94287.385522


# PAT changed @ 94287.385522


# PAT changed @ 94287.385522


# PAT changed @ 24306.266367


# PAT changed @ 24306.266367


# PAT changed @ 24306.266367


# PAT changed @ 24306.266367

^C%

Incorrect pts time

We have an 86MB, 9 to 10 minute video snippet where for the first of 2 SCTE messages, in version 2.2.99 the pcr shows 87726.9313 and the pts shows 87726.952711. But when moving the 2.3.01 or 2.3.03, the pcr shows 87726.9313 but the pts shows 87.381333. When monitoring a live video stream over 10 hours we may see approximately 8 out of 61 Scte35 messages have this exact 87.381333 value instead of the correct value. PCR looks correct in all cases and the same video stream sample under 2.2.99 looks correct as well. So we think there may have been a bug introduced. I probably should not make the video snippet available publicly as it has copyright material in it, but if there is a way we could share it with you privately I would be happy to do so. Thoughts?

Additional question, in the xml output does pts_time already have the pts_adjustment factored in or not? We had assumed not and so were adding it in our calculations, but looking at the code it looks like it might be in which case we would be double adding it.

OSError: [Errno 55] No buffer space available

not sure is anyone encounter this error
but when i try to
strm = threefive.Stream('udp://@224.2.2.4:1234')
i keep getting
OSError: [Errno 55] No buffer space available

ffplay playing good

Input #0, mpegts, from 'udp://@224.2.2.4:1234':KB sq= 0B f=0/0 Duration: N/A, start: 630.728000, bitrate: N/A Program 1 Metadata: service_name : Service01 service_provider: FFmpeg Stream #0:0[0x100]: Video: h264 (High) ([27][0][0][0] / 0x001B), yuv420p(tv, bt709, progressive), 1280x720 [SAR 1:1 DAR 16:9], 25 fps, 25 tbr, 90k tbn Stream #0:1[0x101](und): Audio: aac (LC) ([15][0][0][0] / 0x000F), 48000 Hz, stereo, fltp, 98 kb/s Stream #0:2[0x258]: Data: scte_35

System: macos Big Sur
Python : Python 3.9.13 with Anaconda

thanks

Unparseable hex

Hi there,

I have a stream from MediaLive/MediaPackage that contains the following: 0xFC303B00003BE1341800FFF00506FE00505D700025022343554549000000027FD10000A4D990120D48656C6C6F2C20576F726C64213400000000592280ED

Threefive does not appear to be able to parse it.

In case it helps, it is from a TS file that was generated with TSduck, by inserting the following SCTE information:

<?xml version="1.0" encoding="UTF-8"?>
<tsduck>
    <!-- Event 2 - Start -->
    <splice_information_table protocol_version="0" pts_adjustment="0" tier="0x0FFF">
        <time_signal pts_time="5,400,000"/>
        <splice_avail_descriptor identifier="0x43554549" provider_avail_id="0x00000012" />
        <splice_segmentation_descriptor segmentation_event_id="0x00000002"
            web_delivery_allowed="true"
            no_regional_blackout="false"
            archive_allowed="false"
            device_restrictions="1"
            segmentation_duration="10,803,600"
            segmentation_type_id="0x34"
            segment_num="0"
            segments_expected="0"
            sub_segment_num="0"
            sub_segments_expected="0">
            <segmentation_upid type="0x12">
                48656c6c6f2c20576f726c6421
            </segmentation_upid>
        </splice_segmentation_descriptor>
    </splice_information_table>
    <splice_information_table>
        <time_signal/>
      </splice_information_table>

</tsduck>

multi packet PMT and PAT/PMT with adaptation field not working

Hi,

Current TS and section parsing code doesn't handle:

  1. large PMT sections (>1 TS packet)
  2. multiple table sections in a single TS packet (section starting at arbitrary position in TS payload)
  3. TS packets with smaller payload (adaptation field stuffing)

I am working on it.

Unparsed hex

Hi there,

Another one not parsed by threefive, extracted from a MediaPackage stream: 0xFC302C00000003289800FFF005068000F528F000160214435545490000000100D100002932E00C002200006349D893.

It has a 0x22 BreakStart descriptor with empty UPID (tsduck did not let me insert one without a UPID)

encoding a signal with auto_return = true

Hi Adrian,

I can't tell if this is supposed to be supported or not, but I have a use case where I need to generate a SCTE35 with auto_return = true .

From your code I see various mentions of cmd.break_auto_return, but when I try to encode a SCTE35 (TimeSignal) with this set to True, it seems to be ignored. Please let me know, thanks.

Encoding and Inserting SCTE35 packets into MPEG-TS video

  • I'm about to start adding SCTE35 encoding methods.
    • I created the bitn.NBin class to make encoding data to easy.
    • check this out:
      * The first method _parse_break, is the decoder using bitn.BitBin.
      • _encode_break, the encoder using bitn.NBin follows the same flow as the decoder method.
    def _parse_break(self, bitbin):
        """
        SpliceInsert._parse_break(bitbin) is called
        if SpliceInsert.duration_flag is set
        """
        self.break_auto_return = bitbin.asflag(1)
        bitbin.forward(6)
        self.break_duration = bitbin.as90k(33)

    def _encode_break(self, nbin):
        """
        SpliceInsert._encode_break(nbin) is called
        if SpliceInsert.duration_flag is set
        """
        nbin.add_flag(self.break_auto_return)
        nbin.forward(6)
        nbin.add_90k(self.break_duration, 33)

    ```

cue decode fails with UniCodeDecode error, unhandled exception

When using:

Base64 = "/DBhAAAAAyiY///wBQb+AS4ycQBLAh5DVUVJLcYgPX//AAD4flUICAAEzagtxiA9NAECAAACKUNVRUkAAAAAf78MGlZNTlUB+pOe004CEe2tjg5Azy/ChQGxHMmtAQAAphbmGg=="

cue = threefive.Cue(Base64)

cue.decode()

we get:

File "/Users/xxx/xxx/threefive/bitn.py", line 54, in as_ascii
return int.to_bytes(stuff, wide, byteorder="big").decode("utf-8")
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xfa in position 1: invalid start byte

Where we would expect cue.decode() to return False or fail (?)

'hasp.py' for SCTE-35 HLS parsing throws error "OverflowError: int too big to convert" or "[Errno 104] Connection reset by peer"

Hi @futzu

First of all, great project! :)

When using hasp.py with the following command: pypy3 hasp.py <m3u8_manifest> I keep getting the following errors:

OverflowError: int too big to convert 

and

[Errno 104] Connection reset by peer

I have tried with:

  • Few different Live HLS streams from my side, which have been on-air for the past few years, and also the m3u8 manifest from #37 renamed to "9syd_master1.m3u8.txt" (to comply with github upload). It does not work and throws the above error
  • The 2 m3u8 examples available on your github. It works.

Would you happen to know what could be the issue? I can provide you with the m3u8 manifest that are causing the issue.

ERROR while decoding TS: 21 since v.2.2.09

When running examples/multicast/ts_scte_parser.py I only see ERROR while decoding TS: 21 for each SCTE-35 packet, using v.2.2.25 on Python 3.6. The latest working release is v.2.2.01, v.2.2.09 is the first release with this error.

'hlsparse.py' for SCTE-35 HLS parsing throws error "FileNotFoundError: [Errno 2] No such file or directory"

Hi @futzu

When using hlsparse.py with the following command: pypy3 hlsparse.py <m3u8_manifest> I keep getting the following error:

Traceback (most recent call last):
  File "hlsparse.py", line 12, in <module>
    with open(sys.argv[1], "r") as manifest:
FileNotFoundError: [Errno 2] No such file or directory: 

I have tried with:

  • 5 different Live HLS streams from my side, which have been on-air for the past few years. It does not work and throws the above error
  • The 2 m3u8 examples available on your github. It works.

Would you happen to know what could be the issue? I can provide you with the m3u8 manifest that are causing the issue.

Can't parse valid SCTE-35

Here it is:
/DBlAAAAAAAAAP/wBQb+GVJTDABPcAZNRFNOQzUCRUNVRUkAAKTff8MAACky4A8xdXJuOnV1aWQ6QnJlYWstQjAwMjA4NTU2ODlfMDAxMi0wNy0xMC1YMDExMjUxNjEyNDAAAPkSB7E=

This is valid SCTE-35 found in the wild (in a DSM-CC stream).
It is perfectly parsable with https://comcast.github.io/scte35-js/

For reference, this is the code the HbbTV app uses to parse it:
http://hbbtv.mediaset.net/app/mplayhbbtvgoldzoo/dev/js/component/scte35Parser.js

Looks like threefive doesn't have support for custom descriptors maybe? It seems to define a custom tag=0x70 descriptor.

I tried to do something like this: https://github.com/futzu/scte35-threefive/blob/master/examples/upid/custom_upid_handling.py
but unfortunately it doesn't work since as soon as threefive sees the unhadled tag=0x70 descriptor (not in known descriptor map) it refuses to parse extra stuff. After that there is a regular tag=2 descriptor but it gets ignored, and I get an empty descriptor array in the end. @futzu I guess this last one is a bug, it shouldn't ignore known descriptors.

I'd like to custom-process the tag=0x70 descriptor AND get the content of the tag=2 one

Encoding issues after edit SCTE-35 message Break_Duration

Hello,
First of all, thanks for the tool.

I'm trying to edit a break duration. The example I have is the one from the spec which is the one in your examples but I cannot reproduce the results in that example.
Example reference: https://github.com/futzu/SCTE35_threefive/blob/master/examples/encode/edit_break_duration.py
Note 1: For some reason, using base64 format returns "false" when I try to decode so all I did was using HEX format.

Using Mint. Python3. PIP Fresh installed. threefive fresh installed.
What I do:

$python3
Python 3.10.12 (main, Mar 22 2024, 16:50:05) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import threefive
>>> eds="FC302F000000000000FFFFF014054800008F7FEFFE7369C02EFE0052CCF500000000000A0008435545490000013562DBA30A"
>>> threefive.decode(eds)
{
    "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,
        "pts_adjustment": 0.0,
        "cw_index": "0xff",
        "tier": "0x0fff",
        "splice_command_length": 20,
        "splice_command_type": 5,
        "descriptor_loop_length": 10,
        "crc": "0x62dba30a"
    },
    "command": {
        "command_length": 20,
        "command_type": 5,
        "name": "Splice Insert",
        "time_specified_flag": true,
        "pts_time": 21514.559089,
        "pts_time_ticks": 1936310318,
        "break_auto_return": true,
        "break_duration": 60.293567,
        "break_duration_ticks": 5426421,
        "splice_event_id": 1207959695,
        "splice_event_cancel_indicator": false,
        "out_of_network_indicator": true,
        "program_splice_flag": true,
        "duration_flag": true,
        "splice_immediate_flag": false,
        "event_id_compliance_flag": true,
        "unique_program_id": 0,
        "avail_num": 0,
        "avail_expected": 0
    },
    "descriptors": [
        {
            "tag": 0,
            "descriptor_length": 8,
            "name": "Avail Descriptor",
            "identifier": "CUEI",
            "provider_avail_id": 309
        }
    ]
}
True
>>> cue = threefive.Cue(eds)
>>> cue.decode()
True

The step above returns "false" if I use the base64 SCTE-35 message for some reason. Same result in two different systems.

>>> cue.command.break_duration
60.293567
>>> cue.command.break_duration=90.00
>>> cue.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,
        "pts_adjustment": 0.0,
        "cw_index": "0xff",
        "tier": "0x0fff",
        "splice_command_length": 20,
        "splice_command_type": 5,
        "descriptor_loop_length": 10,
        "crc": "0x62dba30a"
    },
    "command": {
        "command_length": 20,
        "command_type": 5,
        "name": "Splice Insert",
        "time_specified_flag": true,
        "pts_time": 21514.559089,
        "pts_time_ticks": 1936310318,
        "break_auto_return": true,
        "break_duration": 90.0,
        "break_duration_ticks": 5426421,
        "splice_event_id": 1207959695,
        "splice_event_cancel_indicator": false,
        "out_of_network_indicator": true,
        "program_splice_flag": true,
        "duration_flag": true,
        "splice_immediate_flag": false,
        "event_id_compliance_flag": true,
        "unique_program_id": 0,
        "avail_num": 0,
        "avail_expected": 0
    },
    "descriptors": [
        {
            "tag": 0,
            "descriptor_length": 8,
            "name": "Avail Descriptor",
            "identifier": "CUEI",
            "provider_avail_id": 309
        }
    ]
}

>>> cue.encode_as_hex()
'0xfc302f000000000000fffff014054800008f7feffe7369c02efe0052ccf500000000000a0008435545490000013562dba30a'
>>>

Note that the output is the same as original input:

0xFC302F000000000000FFFFF014054800008F7FEFFE7369C02EFE0052CCF500000000000A0008435545490000013562DBA30A
0xfc302f000000000000fffff014054800008f7feffe7369c02efe0052ccf500000000000a0008435545490000013562dba30a

I did a different test using the same SCTE-35 message but instead of editing the break duration I have deleted the descriptors. In that case it worked. I mean, the output is a new HEX SCTE-35 that once decoded has the descriptor removed.

Am I doing something wrong?
Thanks

syntax error in Python 3.4

# pip3 install threefive
Downloading/unpacking threefive
  Downloading threefive-2.1.79-py3-none-any.whl
Requirement already satisfied (use --upgrade to upgrade): bitn>=0.0.27 in /usr/local/lib/python3.4/dist-packages (from threefive)
Installing collected packages: threefive
*** Error compiling '/tmp/pip-build-a1bjun2e/threefive/threefive/segmentation.py'...
  File "/tmp/pip-build-a1bjun2e/threefive/threefive/segmentation.py", line 137
    return f'{upid_map[upid_type][0]}:{upid_id}'
                                               ^
SyntaxError: invalid syntax

Successfully installed threefive
Cleaning up...

ts_scte_parser.py not decoding SCTE packets

Hello,
I'm trying to decode SCTE packets from a transport stream via ts_scte_parser.py but it seems like it is not able to decode SCTE packet in the incoming stream while other applications was able to detect it.

Version: v.2.1.97

I've edited port and mcast_ip as per my requirement

    def __init__(self, mcast_ip, if_ip="0.0.0.0", hostname="0.0.0.0", port=5511):
        self.HOST = hostname

if __name__ == "__main__":
    test = TSScte35Parser(mcast_ip="225.5.1.1")
    test.run()

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.