[Install](https://github.com/futzu/scte35-hls-segmenter-x9k3/README.md#install) |
[Use](#how-to-use) |
[CUE-OUT](#cue-out) |
[CUE-IN](#cue-in) |
[SCTE-35 Tags](#supported-hls--tags) |
[Sidecar SCTE35](#sidecar-files) |
[Byterange m3u8](#byterange) |
[Playlists](#playlists) |
[Live hls](#live) |
[Bugs](https://github.com/futzu/scte35-hls-segmenter-x9k3/issues) |
[Online SCTE-35 encoder](https://iodisco.com/cgi-bin/scte35encoder)

# x9k3 is an HLS segmenter that injects SCTE-35 into m3u8 files.
# Current Version: `v.0.2.57`
### The `only supported version is the current version`. Keep up.
## Heads up! upgrade threefive to `2`.`4`.`55`
```js
python3 -mpip install --upgrade threefive
```
* and / or
```js
pypy3 -mpip install --upgrade threefive
```
* __Some of the new stuff__:
* x9k3 can now generate [byterange](#byterange) m3u8 files with `-b` or `--byterange`
* `Playlists` can now be used as input.
* Segment `start time` is now always read, never calculated.
* Segment `duration verification` for segments that exceed the `target duration`.
* `adbreak` script to generate SCTE-35 Cues.
* `m3u8 files as input`. Resegment and add SCTE-35 to an existing m3u8. `-i INPUT`, `--input INPUT`
* `continue an m3u8 file.` Segments may be added to an existing m3u8, VOD or live. ` -c`, `--continue_m3u8 `
* `discontinuity tags` may now be `omitted`. `-n`, `--no_discontinuity`
* auto `CUE-IN`
* live `throttling` can be `disabled` with the `-N`, `--no_throttle` flag
# `Heads Up`
### `adbreak`
* included with x9k3
* adbreak generates SCTE35 Cues for CUE-OUT and CUE-IN in a sidecar file.
```lua
$ adbreak -h
usage: adbreak [-h] [-d DURATION] [-p PTS] [-s SIDECAR]
options:
-h, --help show this help message and exit
-d DURATION, --duration DURATION
set duration of ad break. [ default: 60.0 ]
-p PTS, --pts PTS set start pts for ad break. Not setting pts will
generate a Splice Immediate CUE-OUT. [ default: 0.0 ]
-s SIDECAR, --sidecar SIDECAR
Sidecar file of SCTE-35 (pts,cue) pairs. [ default:
sidecar.txt ]
```
* Usage:
```lua
adbreak --pts 1234.567890 --duration 30 --sidecar sidecar.txt
```
* sidecar file has SCTE-35 Cues for CUE-OUT and CUE-IN
```lua
1234.56789,/DAlAAAAAAAAAP/wFAUAAAABf+/+Bp9rxv4AKTLgAAEAAAAAhzvmvQ==
1264.56789,/DAgAAAAAAAAAP/wDwUAAAABf0/+BsiepgABAAAAAPh51T0=
```
* pass to x9k3
```lua
x9k3 -i input.ts -s sidecar.txt
```
# `Features`
* __SCTE-35 Cues__ in __Mpegts Streams__ are Translated into __HLS tags__.
* __SCTE-35 Cues can be added from a [Sidecar File](#sidecar-files)__.
* Segments are __Split on SCTE-35 Cues__ as needed.
* Segments __Start on iframes__.
* Supports __h264__ and __h265__ .
* __Multi-protocol.__ Input sources may be __Files, Http(s), Multicast, and Unicast UDP streams__.
* Supports [__Live__](https://github.com/futzu/scte35-hls-x9k3#live) __Streaming__.
* [__amt-play__ ](https://github.com/vivoh-inc/amt-play)uses x9k3.
---
# `Requires`
* python 3.6+ or pypy3
* [threefive](https://github.com/futzu/scte35-threefive)
* [new_reader](https://github.com/futzu/new_reader)
* [iframes](https://github.com/futzu/iframes)
# `Install`
* Use pip to install the the x9k3 lib and executable script x9k3 (_will install threefive, new_reader and iframes too_)
```lua
# python3
python3 -mpip install x9k3
# pypy3
pypy3 -mpip install x9k3
```
[⇪ top](https://github.com/futzu/x9k3/blob/main/README.md#hls--scte35--x9k3)
# `Details`
* __X-SCTE35__, __X-CUE__, __X-DATERANGE__, or __X-SPLICEPOINT__ HLS tags can be generated. set with the `--hls_tag` switch.
* reading from stdin now available
* Segments are cut on iframes.
* Segment time is 2 seconds or more, determined by GOP size. Can be set with the `-t` switch or by setting `X9K3.args.time`
* Segments are named seg1.ts seg2.ts etc...
* For SCTE-35, Video segments are cut at the the first iframe >= the splice point pts.
* If no pts time is present in the SCTE-35 cue, the segment is cut at the next iframe.
* SCTE-35 cues with a preroll are inserted at the splice point.
# `How to Use`
# `cli`
```smalltalk
a@fu:~/x9k3-repo$ x9k3 -h
usage: x9k3 [-h] [-i INPUT] [-b] [-c] [-d] [-l] [-n] [-N] [-o OUTPUT_DIR] [-p]
[-r] [-s SIDECAR_FILE] [-S] [-t TIME] [-T HLS_TAG]
[-w WINDOW_SIZE] [-v]
optional arguments:
-h, --help show this help message and exit
-i INPUT, --input INPUT
The Input video can be mpegts or m3u8 with mpegts
segments, or a playlist with mpegts files and/or
mpegts m3u8 files. The input can be a local video,
http(s), udp, multicast or stdin.
-b, --byterange Flag for byterange hls [default:False]
-c, --continue_m3u8 Resume writing index.m3u8 [default:False]
-d, --delete delete segments (enables --live) [default:False]
-l, --live Flag for a live event (enables sliding window m3u8)
[default:False]
-n, --no_discontinuity
Flag to disable adding #EXT-X-DISCONTINUITY tags at
splice points [default:False]
-N, --no-throttle disable live throttling [default:False]
-o OUTPUT_DIR, --output_dir OUTPUT_DIR
Directory for segments and index.m3u8(created if
needed) [default:'.']
-p, --program_date_time
Flag to add Program Date Time tags to index.m3u8 (
enables --live) [default:False]
-r, --replay Flag for replay aka looping (enables --live,--delete)
[default:False]
-s SIDECAR_FILE, --sidecar_file SIDECAR_FILE
Sidecar file of SCTE-35 (pts,cue) pairs.
[default:None]
-S, --shulga Flag to enable Shulga iframe detection mode
[default:False]
-t TIME, --time TIME Segment time in seconds [default:2]
-T HLS_TAG, --hls_tag HLS_TAG
x_scte35, x_cue, x_daterange, or x_splicepoint
[default:x_cue]
-w WINDOW_SIZE, --window_size WINDOW_SIZE
sliding window size (enables --live) [default:5]
-v, --version Show version
```
### Example Usage
#### `local file as input`
```smalltalk
x9k3 -i video.mpegts
```
#### `multicast stream as input with a live sliding window`
```smalltalk
x9k3 --live -i udp://@235.35.3.5:3535
```
#### Live mode works with a live source or static files.
* x9k3 will throttle segment creation to mimic a live stream.
```js
x9k3 --live -i /some/video.ts
```
#### `live sliding window and deleting expired segments`
```smalltalk
x9k3 -i udp://@235.35.3.5:3535 --delete
```
#### `https stream for input, and writing segments to an output directory`
* directory will be created if it does not exist.
```smalltalk
x9k3 -i https://so.slo.me/longb.ts --output_dir /home/a/variant0
```
#### `https hls m3u8 for input, and inserting SCTE-35 from a sidecar file, and continuing from a previously create index.m3u8 in the output dir`
```smalltalk
x9k3 -i https://slow.golf/longb.m3u8 --output_dir /home/a/variant0 -continue_m3u8 -s sidecar.txt
```
#### `using stdin as input`
```smalltalk
cat video.ts | x9k3
```
#### `live m3u8 file as input, add SCTE-35 from a sidecar file, change segment duration to 3 and output as live stream`
```smalltalk
x9k3 -i https://example.com/rendition.m3u8 -s sidecar.txt -t 3 -l
```
[⇪ top](https://github.com/futzu/x9k3/blob/main/README.md#hls--scte35--x9k3)
# `Programmatically`
## `Up and Running in three Lines`
```smalltalk
from x9k3 import X9K3 # 1
x9 = X9K3('/home/a/cool.ts') # 2
x9.decode() # 3
```
#### Writing Code with x9k3
* you can get a complete set of args and the defaults like this
```js
from x9k3 import X9K3
x9 = X9K3()
>>>> {print(k,':',v) for k,v in vars(x9.args).items()}
input : <_io.BufferedReader name=''>
continue_m3u8 : False
delete : False
live : False
no_discontinuity : False
no_throttle : False
output_dir : .
program_date_time : False
replay : False
sidecar_file : None
shulga : False
time : 2
hls_tag : x_cue
window_size : 5
version : False
```
* or just
```lua
>>>> print(x9.args)
Namespace(input=<_io.BufferedReader name=''>, continue_m3u8=False, delete=False, live=False, no_discontinuity=False, no_throttle=False, output_dir='.', program_date_time=False, replay=False, sidecar_file=None, shulga=False, time=2, hls_tag='x_cue', window_size=5, version=False)
```
* Setting parameters
```js
from x9k3 import X9K3
x9 = X9K3()
```
* `input source`
```smalltalk
x9.args.input = "https://futzu.com/xaa.ts"
```
* `hls_tag` can be x_scte35, x_cue, x_daterange, or x_splicepoint
```smalltalk
x9.args.hls tag = x_cue
```
* `output directory` default is "."
```js
x9.args.output_dir="/home/a/stuff"
```
* `live`
```smalltalk
x9.args.live = True
```
* `replay` (loop video) ( also sets live )
```js
x9.args.replay = True
```
* `delete` segments when they expire ( also sets live )
```js
x9.args.delete = True
```
* add `program date time` tags ( also sets live )
```js
x9.args.program_date_time= True
```
* set `window size` for live mode ( requires live )
```js
x9.args.window_size = 5
```
* run
```js
x9.decode()
```
[⇪ top](https://github.com/futzu/x9k3/blob/main/README.md#hls--scte35--x9k3)
### `byterange`
* with the cli tool
* use __full path to video file__ when creating byterange m3u8.
```smalltalk
x9k3 -i /home/a/input.ts -b
```
* programmatically
```smalltalk
from x9k3 import X9K3
x9 = X9K3()
x9.self.args.byterange = True
x9.decode()
```
* output
```lua
#EXTM3U
#EXT-X-VERSION:4
#EXT-X-TARGETDURATION:3
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-DISCONTINUITY-SEQUENCE:0
#EXT-X-X9K3-VERSION:0.2.55
#EXTINF:2.000000,
#EXT-X-BYTERANGE:135548@0
msnbc1000.ts
#EXTINF:2.000000,
#EXT-X-BYTERANGE:137992@135548
msnbc1000.ts
#EXTINF:2.000000,
#EXT-X-BYTERANGE:134796@273540
msnbc1000.ts
#EXTINF:2.000000,
#EXT-X-BYTERANGE:140436@408336
msnbc1000.ts
#EXTINF:2.000000,
#EXT-X-BYTERANGE:130096@548772
msnbc1000.ts
```
### `playlists`
* playlists can be used as input
* playlist files must end in `.playlist`
* lines are video or video, sidecar
* if video,sidecar, the sidecar file only applies to that video
* playlists can have mpegts video, mpegts m3u8, and playlists.
* example playlist
```lua
f10.ts,f10sidecar.txt # comments can be here
f17.ts
f60.ts
flat-striped.ts
# Comments can go here too.
flat.ts
input.ts
nmax.ts
nmx.ts,nmx-sidecar.txt
https://futzu.com/xaa.ts
https://example.com/index.m3u8,another-sidecar.txt
```
* using
```lua
x9k3 -i out.playlist
```
### `Sidecar Files`
#### load scte35 cues from a Sidecar file
Sidecar Cues will be handled the same as SCTE35 cues from a video stream.
line format for text file `insert_pts, cue`
pts is the insert time for the cue, A four second preroll is standard.
cue can be base64,hex, int, or bytes
```smalltalk
a@debian:~/x9k3$ cat sidecar.txt
38103.868589, /DAxAAAAAAAAAP/wFAUAAABdf+/+zHRtOn4Ae6DOAAAAAAAMAQpDVUVJsZ8xMjEqLYemJQ==
38199.918911, /DAsAAAAAAAAAP/wDwUAAABef0/+zPACTQAAAAAADAEKQ1VFSbGfMTIxIxGolm0=
```
```smalltalk
x9k3 -i noscte35.ts -s sidecar.txt
```
#### In Live Mode you can do dynamic cue injection with a `Sidecar file`
```js
touch sidecar.txt
x9k3 -i vid.ts -s sidecar.txt -l
# Open another terminal and printf cues into sidecar.txt
printf '38103.868589, /DAxAAAAAAAAAP/wFAUAAABdf+/+zHRtOn4Ae6DOAAAAAAAMAQpDVUVJsZ8xMjEqLYemJQ==\n' > sidecar.txt
```
#### `Sidecar files` can now accept 0 as the PTS insert time for Splice Immediate.
Specify 0 as the insert time, the cue will be insert at the start of the next segment.
__Using 0 only works in live mode__
```js
printf '0,/DAhAAAAAAAAAP/wEAUAAAAJf78A/gASZvAACQAAAACokv3z\n' > sidecar.txt
```
[⇪ top](https://github.com/futzu/x9k3/blob/main/README.md#hls--scte35--x9k3)
#### A CUE-OUT can be terminated early using a `sidecar file`.
In the middle of a CUE-OUT send a splice insert with the out_of_network_indicator flag not set and the splice immediate flag set.
Do the steps above ,
and then do this
```js
printf '0,/DAcAAAAAAAAAP/wCwUAAAABfx8AAAEAAAAA3r8DiQ==\n' > sidecar.txt
```
It will cause the CUE-OUT to end at the next segment start.
```js
#EXT-X-CUE-OUT 13.4
./seg5.ts: start:112.966667 end:114.966667 duration:2.233334
#EXT-X-CUE-OUT-CONT 2.233334/13.4
./seg6.ts: start:114.966667 end:116.966667 duration:2.1
#EXT-X-CUE-OUT-CONT 4.333334/13.4
./seg7.ts: start:116.966667 end:118.966667 duration:2.0
#EXT-X-CUE-OUT-CONT 6.333334/13.4
./seg8.ts: start:117.0 end:119.0 duration:0.033333
#EXT-X-CUE-IN None
./seg9.ts: start:119.3 end:121.3 duration:2.3
```
__Using 0 only works in live mode__
---
## CUES
## `CUE-OUT`
#### A CUE-OUT is defined as:
* `A Splice Insert Command` with:
* the `out_of_network_indicator` set to `True`
* a `break_duration`.
* `A Time Signal Command` and a Segmentation Descriptor with:
* a `segmentation_duration`
* a `segmentation_type_id` of:
* 0x22: "Break Start",
* 0x30: "Provider Advertisement Start",
* 0x32: "Distributor Advertisement Start",
* 0x34: "Provider Placement Opportunity Start",
* 0x36: "Distributor Placement Opportunity Start",
* 0x44: "Provider Ad Block Start",
* 0x46: "Distributor Ad Block Start",
[⇪ top](https://github.com/futzu/x9k3/blob/main/README.md#hls--scte35--x9k3)
## `CUE-IN`
#### A CUE-IN is defined as:
* `A Splice Insert Command`
* with the `out_of_network_indicator` set to `False`
* `A Time Signal Command` and a Segmentation Descriptor with:
* a `segmentation_type_id` of:
* 0x23: "Break End",
* 0x31: "Provider Advertisement End",
* 0x33: "Distributor Advertisement End",
* 0x35: "Provider Placement Opportunity End",
* 0x37: "Distributor Placement Opportunity End",
* 0x45: "Provider Ad Block End",
* 0x47: "Distributor Ad Block End",
* For CUE-OUT and CUE-IN, `only the first Segmentation Descriptor will be used`
---
## `Supported HLS Tags`
* #EXT-X-CUE
* #EXT-X-DATERANGE
* #EXT-X-SCTE35
* #EXT-X-SPLICEPOINT
### `x_cue`
* CUE-OUT
```lua
#EXT-X-DISCONTINUITY
#EXT-X-CUE-OUT:242.0
#EXTINF:4.796145,
seg32.ts
```
* CUE-OUT-CONT
```lua
#EXT-X-CUE-OUT-CONT:4.796145/242.0
#EXTINF:2.12,
```
* CUE-IN
```lua
#EXT-X-DISCONTINUITY
#EXT-X-CUE-IN
#EXTINF:5.020145,
seg145.ts
```
### `x_scte35`
* CUE-OUT
```lua
#EXT-X-DISCONTINUITY
#EXT-X-SCTE35:CUE="/DAvAAAAAAAAAP/wFAUAAAKWf+//4WoauH4BTFYgAAEAAAAKAAhDVUVJAAAAAOv1oqc=" ,CUE-OUT=YES
#EXTINF:4.796145,
seg32.ts
```
* CUE-OUT-CONT
```lua
#EXT-X-SCTE35:CUE="/DAvAAAAAAAAAP/wFAUAAAKWf+//4WoauH4BTFYgAAEAAAAKAAhDVUVJAAAAAOv1oqc=" ,CUE-OUT=CONT
#EXTINF:2.12,
seg33.ts
```
* CUE-IN
```lua
#EXT-X-DISCONTINUITY
#EXT-X-SCTE35:CUE="/DAqAAAAAAAAAP/wDwUAAAKWf0//4rZw2AABAAAACgAIQ1VFSQAAAAAtegE5" ,CUE-IN=YES
#EXTINF:5.020145,
seg145.ts
```
### `x_daterange`
* CUE-OUT
```lua
#EXT-X-DISCONTINUITY
#EXT-X-DATERANGE:ID="1",START-DATE="2022-10-14T17:36:58.321731Z",PLANNED-DURATION=242.0,SCTE35-OUT=0xfc302f00000000000000fff01405000002967fefffe16a1ab87e014c562000010000000a00084355454900000000ebf5a2a7
#EXTINF:4.796145,
seg32.ts
```
* CUE-IN
```lua
#EXT-X-DISCONTINUITY
#EXT-X-DATERANGE:ID="2",END-DATE="2022-10-14T17:36:58.666073Z",SCTE35-IN=0xfc302a00000000000000fff00f05000002967f4fffe2b670d800010000000a000
843554549000000002d7a0139
#EXTINF:5.020145,
seg145.ts
```
### `x_splicepoint`
* CUE-OUT
```lua
#EXT-X-DISCONTINUITY
#EXT-X-SPLICEPOINT-SCTE35:/DAvAAAAAAAAAP/wFAUAAAKWf+//4WoauH4BTFYgAAEAAAAKAAhDVUVJAAAAAOv1oqc=
#EXTINF:4.796145,
seg32.ts
```
* CUE-IN
```lua
#EXT-X-DISCONTINUITY
#EXT-X-SPLICEPOINT-SCTE35:/DAqAAAAAAAAAP/wDwUAAAKWf0//4rZw2AABAAAACgAIQ1VFSQAAAAAtegE5
#EXTINF:5.020145,
seg145.ts
```
[⇪ top](https://github.com/futzu/x9k3/blob/main/README.md#hls--scte35--x9k3)
## `VOD`
* x9k3 defaults to VOD style playlist generation.
* All segment are listed in the m3u8 file.
## `Live`
* Activated by the `--live`, `--delete`, or `--replay` switch or by setting `X9K3.live=True`
### `--live`
* Like VOD except:
* M3u8 manifests are regenerated every time a segment is written
* Segment creation is throttled when using non-live sources to simulate live streaming. ( like ffmpeg's "-re" )
* default Sliding Window size is 5, it can be changed with the `-w` switch or by setting `X9k3.window.size`
### `--delete`
* implies `--live`
* deletes segments when they move out of the sliding window of the m3u8.
### `--replay`
* implies `--live`
* implies `--delete`
* loops a video file and throttles segment creation to fake a live stream.
[⇪ top](https://github.com/futzu/x9k3/blob/main/README.md#hls--scte35--x9k3)
