Closed josepowera closed 2 years ago
I don't see why not. I currently use video iframes to split segments, let me see what I can work out for audio.
Adrian
I had no problem doing it, let me know if this works for you.
ffmpeg -copyts -i video.ts -vn -c copy audio.ts
a@debian:~/build/x9k3$ ffprobe -hide_banner audio.ts Input #0, mpegts, from 'audio.ts': Duration: 00:48:04.10, start: 4.220300, bitrate: 159 kb/s Program 1 Metadata: service_name : Service01 service_provider: FFmpeg Stream #0:00x100: Audio: aac (LC) ([15][0][0][0] / 0x000F), 44100 Hz, stereo, fltp, 108 kb/s
* I made a sidecar file of PTS, SCTE35 Cue pairs
```lua
a@debian:~/build/x9k3$ cat sidecar.txt
102.805,/DAlAAAAAAAAAP/wFAUAAAABf+/+AJKsov4ADdtEAAEAAAAA7eV+Kg==
177.6,/DAlAAAAAAAAAP/wFAUAAAACf+/+APljwP4ADdtEAAIAAAAACcxDcg====
411.0,/DAlAAAAAAAAAP/wFAUAAAADf+/+AjnqcP4ADdtEAAMAAAAASnrfIQ====
927.697,/DAlAAAAAAAAAP/wFAUAAAAEf+/+BP99uv4ADdtEAAQAAAAAtZKbsQ==
1286.52,/DAlAAAAAAAAAP/wFAUAAAAFf+/+BuxCcP4ADdtEAAUAAAAAxSzhag==
2190.39,/DAlAAAAAAAAAP/wFAUAAAAGf+/+C8WJPP4ADdtEAAYAAAAAM8x/Xw==
2807.71,/DAlAAAAAAAAAP/wFAUAAAAHf+/+DxVLzP4ADdtEAAcAAAAAljzkJg==
2891.63,/DAlAAAAAAAAAP/wFAUAAAAIf+/+D4iK7P4ADdtEAAgAAAAAR8yZag==
x9k3 -i audio.ts -s sidecar.txt
a@debian:~/build/x9k3$ grep -1 CUE-OUT=YES index.m3u8
# Splice Point @ 106.805
#EXT-X-SCTE35:CUE="/DAlAAAAAAAAAP/wFAUAAAABf+/+AJKsov4ADdtEAAEAAAAA7eV+Kg==",CUE-OUT=YES
#EXTINF:2.0898,
--
# Splice Point @ 181.6
#EXT-X-SCTE35:CUE="/DAlAAAAAAAAAP/wFAUAAAACf+/+APljwP4ADdtEAAIAAAAACcxDcg==",CUE-OUT=YES
#EXTINF:2.136233,
--
# Splice Point @ 415.0
#EXT-X-SCTE35:CUE="/DAlAAAAAAAAAP/wFAUAAAADf+/+AjnqcP4ADdtEAAMAAAAASnrfIQ==",CUE-OUT=YES
#EXTINF:2.159455,
--
# Splice Point @ 931.697
#EXT-X-SCTE35:CUE="/DAlAAAAAAAAAP/wFAUAAAAEf+/+BP99uv4ADdtEAAQAAAAAtZKbsQ==",CUE-OUT=YES
#EXTINF:2.112989,
--
# Splice Point @ 1290.52
#EXT-X-SCTE35:CUE="/DAlAAAAAAAAAP/wFAUAAAAFf+/+BuxCcP4ADdtEAAUAAAAAxSzhag==",CUE-OUT=YES
#EXTINF:2.136234,
--
# Splice Point @ 2194.39
#EXT-X-SCTE35:CUE="/DAlAAAAAAAAAP/wFAUAAAAGf+/+C8WJPP4ADdtEAAYAAAAAM8x/Xw==",CUE-OUT=YES
#EXTINF:2.136256,
--
# Splice Point @ 2811.71
#EXT-X-SCTE35:CUE="/DAlAAAAAAAAAP/wFAUAAAAHf+/+DxVLzP4ADdtEAAcAAAAAljzkJg==",CUE-OUT=YES
#EXTINF:2.020144,
* tested in ffplay
ffplay index.m3u8
Will try this and come back (have to dev first the sidecar generator using live not file -extract PTS and auto use that to generate sidecar)
This should help, https://github.com/futzu/scte35-threefive/blob/master/Encoding.md let me know if you have any questions.
@josepowera
This is what I use to generate splice inserts.
from threefive import Cue,SpliceInsert
def mk_cue(event_id, pts, duration=None):
"""
mk_cue make a SCTE-35 Cue
with a Splice Insert command.
set duration to make a CUE-OUT,
"""
pts= float(pts)
cue = Cue()
# default is a CUE-IN
sin = SpliceInsert()
sin.splice_event_id = event_id
sin.splice_event_cancel_indicator = False
sin.out_of_network_indicator = False
sin.time_specified_flag = False
sin.program_splice_flag = True
sin.duration_flag = False
sin.splice_immediate_flag = True
sin.unique_program_id = event_id
sin.avail_num = 0
sin.avail_expected = 0
# If we have a duration, make a CUE-OUT
if duration is not None:
sin.time_specified_flag = True
sin.time_specified_flag = True
sin.break_duration = float(duration)
sin.break_auto_return = True
sin.break_duration = duration
sin.splice_immediate_flag = False
sin.duration_flag = True
sin.out_of_network_indicator = True
sin.pts_time = pts
cue.command = sin # Add SpliceInsert to the SCTE35 cue
cue_string = cue.encode() # Use cue.encode_as_hex() for hex instead of base64
cue.decode()
return pts,cue_string
This will be in the next threefive release, you can use it now.
"""
encode.py
threefive.encode has helper functions for Cue encoding.
"""
from threefive.commands import SpliceNull, SpliceInsert, TimeSignal
from threefive.cue import Cue
def mk_splice_null():
"""
mk_splice_null returns a Cue
with a Splice Null
"""
cue = Cue()
sn = SpliceNull()
cue.command = sn
cue.encode()
return cue
def mk_time_signal(pts=None):
"""
mk_time_signal returns a Cue
with a Time Signal
if pts is NOT set:
time_specified_flag False
if pts IS set:
time_specified_flag True
pts_time pts
"""
cue = Cue()
ts = TimeSignal()
ts.time_specified_flag = False
if pts:
pts = float(pts)
ts.time_specified_flag = True
ts.pts_time = pts
cue.command = ts
cue.encode()
return cue
def mk_splice_insert(event_id, pts, duration=None):
"""
mk_cue returns a Cue
with a Splice Insert.
splice_event_id = event_id
If duration is NOT set,
out_of_network_indicator False
time_specified_flag False
duration_flag False
splice_immediate_flag True
if duration IS set:
out_of_network_indicator True
time_specified_flag True
duration_flag True
splice_immediate_flag False
break_auto_return True
break_duration duration
pts_time pts
"""
pts = float(pts)
cue = Cue()
# default is a CUE-IN
sin = SpliceInsert()
sin.splice_event_id = event_id
sin.splice_event_cancel_indicator = False
sin.out_of_network_indicator = False
sin.time_specified_flag = False
sin.program_splice_flag = True
sin.duration_flag = False
sin.splice_immediate_flag = True
sin.unique_program_id = event_id
sin.avail_num = 0
sin.avail_expected = 0
# If we have a duration, make a CUE-OUT
if duration is not None:
duration = float(duration)
sin.time_specified_flag = True
sin.break_duration = duration
sin.break_auto_return = True
sin.splice_immediate_flag = False
sin.duration_flag = True
sin.out_of_network_indicator = True
sin.pts_time = pts
cue.command = sin # Add SpliceInsert to the SCTE35 cue
cue.encode()
return cue
One more thing I'm searching for (I'm still working on this):
-get combination of PTS (could be begining of first segment for each m3u8) and PROGRAM-DATE-TIME. Even if written to console.
-written EXT-X-PROGRAM-DATE-TIME for live streams
-CUE-OUT-CONT currentTime/totalDuration for segments in the middle of cue-out/ cut-in
I have to say, #EXT-X-PROGRAM-DATE-TIME makes no sense to me. If it's a "live" stream wouldn't the time be the current time?
import datetime
from x9k3 import X9K3
class X9K4(X9K3): """ X9K4 is X9K3 with #EXT-X-PROGRAM-DATE-TIME """ def _write_segment(self): """ _write_segment creates segment file, writes segment meta data to self.active_data """ if not self.start: return seg_file = f"seg{self.seg.seg_num}.ts" self.seg.seg_uri = self.mk_uri(self.output_dir, seg_file) if self.seg.seg_stop: self.seg.seg_time = round(self.seg.seg_stop - self.seg.seg_start, 6) if self.live: self.cue_out_continue()
self.active_data.write(f'# PTS {round(self.seg.seg_start, 6)}\n')
iso8601 = f"{datetime.datetime.utcnow().isoformat()}Z"
pdt = f"#EXT-X-PROGRAM-DATE-TIME:{iso8601}"
self.active_data.write(pdt + "\n")
if self.scte35.cue_tag:
self.active_data.write(self.scte35.cue_tag + "\n")
self.scte35.cue_tag = None
with open(self.seg.seg_uri, "wb+") as a_seg:
a_seg.write(self.active_segment.getbuffer())
a_seg.flush()
del self.active_segment
self.active_data.write(f"#EXTINF:{self.seg.seg_time},\n")
self.active_data.write(seg_file + "\n")
self.seg.seg_start = self.seg.seg_stop
self.seg.seg_stop += self.seconds
self.window.append(
(self.seg.seg_num, self.seg.seg_uri, self.active_data.getvalue())
)
self.seg.seg_num += 1
if name == "main": x9k = X9K4() x9k.run()
* Output
```smalltalk
# PTS 21.286556
#EXT-X-PROGRAM-DATE-TIME:2022-10-12T01:47:06.666295Z
#EXTINF:2.002,
seg9.ts
I'm redoing the tag stuff. It will solve the CONT issues. Probably less than a week for me to finish it,
Are you good on this? I'm going to close it if you are, let me know.
Yes, also HLS tag option was a great addition. Will reopen another issue as I move forward.
Just regarding #EXT-X-PROGRAM-DATE-TIME - I still believe it is useful for live. If for live stream you know that it has to CUE-OUT on example 06:00:00.000AM you can calculate where this is when you combine PROGRAM-DATE-TIME and segment duration to get correct PTS. Without PROGRAM-DATE-TIME you don't know what delay it took in encoding and have no reference to real clock.
Regarding CUE-IN and #EXT-X-DISCONTINUITY tag. As I see example below, #EXT-X-DISCONTINUITY should be on next segment after break not last segment of ad-break. I'm working on live stream not VOD.
using x9ks latest from pip (0.1.45):
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:3
#EXT-X-MEDIA-SEQUENCE:1566
#PTS 3453.421333
#EXTINF:2.0,
seg1566.ts
#EXT-X-DISCONTINUITY
# Splice Point @ 3456.421333
#EXT-X-CUE-OUT:6.0
#PTS 3457.454667
#EXTINF:3.966666,
seg1567.ts
#EXT-X-DISCONTINUITY
# Splice Point @ 3462.421333
#EXT-X-CUE-IN
#PTS 3463.454667
#EXTINF:3.966666,
seg1568.ts
#PTS 3467.421333
#EXTINF:2.0,
https://stackoverflow.com/questions/47047919/how-does-ext-x-discontinuity-sequence-tag-work-in-hls-m3u8-file https://developer.apple.com/documentation/http_live_streaming/example_playlists_for_http_live_streaming/incorporating_ads_into_a_playlist example:
#EXTM3U
#EXT-X-TARGETDURATION:10
#EXT-X-VERSION:4
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.0,
ad0.ts
#EXTINF:8.0,
ad1.ts
#EXT-X-DISCONTINUITY
#EXTINF:10.0,
movieA.ts
#EXTINF:10.0,
movieB.ts
Maybe it makes no reason to add discontinuity in x9k3, since discontinuity is needed only if CUE-OUT is actually used (not skipped). In that case application replacing content can also update (add) #EXT-X-DISCONTINUITY.
Are automatic returns (CUE-IN) after CUE-OUT duration supported. At the moment when we add CUE-OUT but no CUE-IN to sidecar -> cue-out-cont will go over cue-out duration. Maybe just add some warning in console if SCTE-35 has "break_auto_return": true, that auto_return will not run.
#EXT-X-TARGETDURATION:3
#EXT-X-MEDIA-SEQUENCE:1876
#EXT-X-CUE-OUT-CONT:11.966666/6.0
#PTS 4941.421333
#EXTINF:2.0,
seg1876.ts
#EXT-X-CUE-OUT-CONT:13.966666/6.0
#PTS 4943.421333
#EXTINF:2.0,
seg1877.ts
#EXT-X-CUE-OUT-CONT:15.966666/6.0
#PTS 4945.421333
used scte-35
{
"info_section": {
"table_id": "0xfc",
"section_syntax_indicator": false,
"private": false,
"sap_type": "0x3",
"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": "0x0",
"tier": "0xfff",
"splice_command_length": 20,
"splice_command_type": 5,
"descriptor_loop_length": 10,
"crc": "0x14e4df6a"
},
"command": {
"command_length": 20,
"command_type": 5,
"name": "Splice Insert",
"time_specified_flag": true,
"pts_time": 5070.421333,
"pts_time_ticks": 456337920,
"break_auto_return": true,
"break_duration": 6.0,
"break_duration_ticks": 540000,
"splice_event_id": 19,
"splice_event_cancel_indicator": false,
"out_of_network_indicator": true,
"program_splice_flag": true,
"duration_flag": true,
"splice_immediate_flag": false,
"unique_program_id": 2,
"avail_num": 0,
"avail_expected": 0
},
"descriptors": [
{
"tag": 0,
"descriptor_length": 8,
"name": "Avail Descriptor",
"identifier": "CUEI",
"provider_avail_id": 0
}
]
}
'Regarding CUE-IN and #EXT-X-DISCONTINUITY tag. As I see example below, #EXT-X-DISCONTINUITY should be on next segment after break not last segment of ad-break. I'm working on live stream not VOD.'
Damn it, I meant to fix that, I saw that too.
'Are automatic returns (CUE-IN) after CUE-OUT duration supported. At the moment when we add CUE-OUT but no CUE-IN to sidecar -> cue-out-cont will go over cue-out duration. Maybe just add some warning in console if SCTE-35 has "break_auto_return": true, that auto_return will not run.'
Yeah, I see what you're saying, I agree that needs to be fixed, but it probably wont be anytime soon.
I showed you how to add the #EXT-X-PROGRAM-DATE-TIME and PTS if you want it, you might want to look at the stream_diff in the output.
./seg3.ts start: 8.868422 duration: 2.002000 stream diff: 1.950796
Stream diff is the difference between real time playback and segment generation. The stream diff above means that the last segment was generated 1.950796 seconds ahead of real time playback, As long as the stream diff stays between -2 and +2, the stream should play without error.
90% of the time, x9k3 is sleeping to slow itself down to keep the sliding window in sync.
I cannot assume that an application will add a DISCONTINUITY and if it's not in place the stream tends to break.
Look, you make a lot of valid points, but I just don't have time to implement all the features you want, if you want to hire me, I can get on it next week, if not, it won't be any time soon. I am just being honest with you.
I fully understand your point of view. My ideas are mostly brainstorming after I'm working around using TS splicing in x9k3 for a few days. Hope more users come to use and find x9k3 useful! Maybe just add license header to source files since I see you already declared MIT license on PIP (https://pypi.org/).
I think I finally fixed the CUE-IN DISCONTINUITY bug.
pypy3 -mpip install --upgrade x9k3
a@debian:~/build/x9k3$ pypy3 x9k3 -h
usage: x9k3.py [-h] [-i INPUT] [-o OUTPUT_DIR] [-s SIDECAR] [-t TIME]
[-T HLS_TAG] [-w WINDOW_SIZE] [-d] [-l] [-r] [-v] [-p]
optional arguments:
-h, --help show this help message and exit
-i INPUT, --input INPUT
Input source, like "/home/a/vid.ts" or
"udp://@235.35.3.5:3535" or "https://futzu.com/xaa.ts"
-o OUTPUT_DIR, --output_dir OUTPUT_DIR
Directory for segments and index.m3u8 ( created if it
does not exist )
-s SIDECAR, --sidecar SIDECAR
Sidecar file of scte35 cues. each line contains PTS,
Cue
-t TIME, --time TIME Segment time in seconds ( default is 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(default:5)
-d, --delete delete segments ( enables --live )
-l, --live Flag for a live event ( enables sliding window m3u8 )
-r, --replay Flag for replay (looping) ( enables --live and
--delete )
-v, --version Show version
-p, --program_date_time
Flag to add Program Date Time tags to index.m3u8 (
enables --live)
I think I finally fixed the CUE-IN DISCONTINUITY bug.
I'm afraid that #EXT-X-DISCONTINUITY is still 1 segment too fast (I'm running on v.0.1.59). As I understand it should be on seg4967.ts and not on seg4966.ts.
I'm running in HLS LIVE mode with cue-out and cue-in signals sent to sidecar.
current version:
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:3
#EXT-X-MEDIA-SEQUENCE:4963
#EXT-X-DISCONTINUITY-SEQUENCE:702
# Splice Point @ 10448.823856
#EXT-X-DISCONTINUITY
#Iframe @ 10448.823856
#EXT-X-PROGRAM-DATE-TIME:2022-10-27T17:59:21.292480Z
#EXT-X-SCTE35:CUE="/DAvAAAAAAAAAP/wFAUAAAGPf+/+OAt8Yf4ACD1gAAIAAAAKAAhDVUVJAAAAAOynsiA=" ,CUE-OUT=YES
#EXTINF:2.090,
seg4963.ts
#Iframe @ 10451.099411
#EXT-X-PROGRAM-DATE-TIME:2022-10-27T17:59:23.590936Z
#EXT-X-SCTE35:CUE="/DAvAAAAAAAAAP/wFAUAAAGPf+/+OAt8Yf4ACD1gAAIAAAAKAAhDVUVJAAAAAOynsiA=" ,CUE-OUT=CONT
#EXTINF:2.067,
seg4964.ts
#Iframe @ 10453.142767
#EXT-X-PROGRAM-DATE-TIME:2022-10-27T17:59:25.594458Z
#EXT-X-SCTE35:CUE="/DAvAAAAAAAAAP/wFAUAAAGPf+/+OAt8Yf4ACD1gAAIAAAAKAAhDVUVJAAAAAOynsiA=" ,CUE-OUT=CONT
#EXTINF:2.043,
seg4965.ts
# Splice Point @ 10455.186122
#EXT-X-DISCONTINUITY
#Iframe @ 10455.186122
#EXT-X-PROGRAM-DATE-TIME:2022-10-27T17:59:27.637347Z
#EXT-X-SCTE35:CUE="/DAqAAAAAAAAAP/wDwUAAAGPf0/+OBO5wQACAAAACgAIQ1VFSQAAAADsueIZ" ,CUE-IN=YES
#EXTINF:2.043,
seg4966.ts
#Iframe @ 10457.4849
#EXT-X-PROGRAM-DATE-TIME:2022-10-27T17:59:29.957457Z
#EXTINF:2.090,
seg4967.ts
I believe #EXT-X-DISCONTINUITY should be on seg4967.ts and not seg4966.ts (yes I know if this is last segment for VOD you simple can't write it)
seg4965.ts
# Splice Point @ 10455.186122
#Iframe @ 10455.186122
#EXT-X-PROGRAM-DATE-TIME:2022-10-27T17:59:27.637347Z
#EXT-X-SCTE35:CUE="/DAqAAAAAAAAAP/wDwUAAAGPf0/+OBO5wQACAAAACgAIQ1VFSQAAAADsueIZ" ,CUE-IN=YES
#EXTINF:2.043,
seg4966.ts
#Iframe @ 10457.4849
#EXT-X-DISCONTINUITY
#EXT-X-PROGRAM-DATE-TIME:2022-10-27T17:59:29.957457Z
#EXTINF:2.090,
seg4967.ts
Is supported to make HLS with SCTE-35 from TS with audio only (radio station) with x9k3?