Skip to content

Commit eb59242

Browse files
committed
feat: add HLS streaming support with --hls option
- Add HLSSegmenter class for WebVTT segmentation with 60-second default segments - Implement M3U8 playlist generation with HLS Version 6 for subtitle support - Add --hls CLI option accepting directory path for multi-file output - Include comprehensive unit tests achieving 100% coverage - Add HLS.js browser integration tests for compatibility validation - Support multi-segment streams with proper duration and timing - Generate X-TIMESTAMP-MAP headers for HLS compatibility - Create ADR-002 documenting decision to implement custom segmentation Resolves limitations with Apple's mediasubtitlesegmenter tool which cannot handle JSON metadata lines exceeding character limits.
1 parent 37ee626 commit eb59242

File tree

11 files changed

+1300
-7
lines changed

11 files changed

+1300
-7
lines changed

/fileSequence0.webvtt

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
WEBVTT
2+
X-TIMESTAMP-MAP=MPEGTS:900000,LOCAL:00:00:00.000
3+
4+
00:00:00.000 --> 00:00:01.000
5+
[{"metric":"distance","value":2.7}]
6+
7+
00:00:01.000 --> 00:00:02.000
8+
[{"metric":"heartRate","value":88}]
9+
10+
00:00:02.000 --> 00:00:03.000
11+
[{"metric":"distance","value":11.8}]
12+
13+
00:00:03.000 --> 00:00:04.000
14+
[{"metric":"cadence","value":25}]
15+
16+
00:00:04.000 --> 00:00:05.000
17+
[{"metric":"heartRate","value":90}]
18+
19+
00:00:05.000 --> 00:00:06.000
20+
[{"metric":"power","value":121}]
21+
22+
00:00:06.000 --> 00:00:07.000
23+
[{"metric":"distance","value":20.7}]
24+
25+
00:00:07.000 --> 00:00:08.000
26+
[{"metric":"cadence","value":24}]
27+
28+
00:00:08.000 --> 00:00:09.000
29+
[{"metric":"heartRate","value":93}]
30+
31+
00:00:09.000 --> 00:00:10.000
32+
[{"metric":"power","value":126}]
33+
34+
00:00:10.000 --> 00:00:11.000
35+
[{"metric":"distance","value":27.8}]
36+
37+
00:00:11.000 --> 00:00:12.000
38+
[{"metric":"cadence","value":24}]
39+
40+
00:00:12.000 --> 00:00:13.000
41+
[{"metric":"heartRate","value":97}]
42+
43+
00:00:13.000 --> 00:00:14.000
44+
[{"metric":"power","value":126}]
45+
46+
00:00:14.000 --> 00:00:15.000
47+
[{"metric":"distance","value":38.5}]
48+
49+
00:00:15.000 --> 00:00:16.000
50+
[{"metric":"cadence","value":24}]
51+
52+
00:00:16.000 --> 00:00:17.000
53+
[{"metric":"heartRate","value":103}]
54+
55+
00:00:17.000 --> 00:00:18.000
56+
[{"metric":"power","value":117}]
57+
58+
00:00:18.000 --> 00:00:19.000
59+
[{"metric":"distance","value":45.3}]
60+
61+
00:00:19.000 --> 00:00:20.000
62+
[{"metric":"cadence","value":24}]
63+
64+
00:00:20.000 --> 00:00:21.000
65+
[{"metric":"heartRate","value":108}]
66+
67+
00:00:21.000 --> 00:00:22.000
68+
[{"metric":"power","value":117}]
69+
70+
00:00:22.000 --> 00:00:23.000
71+
[{"metric":"distance","value":56.1}]
72+
73+
00:00:23.000 --> 00:00:24.000
74+
[{"metric":"cadence","value":23}]
75+
76+
00:00:24.000 --> 00:00:25.000
77+
[{"metric":"heartRate","value":110}]
78+
79+
00:00:25.000 --> 00:00:26.000
80+
[{"metric":"power","value":124}]
81+
82+
00:00:26.000 --> 00:00:27.000
83+
[{"metric":"distance","value":64.9}]
84+
85+
00:00:27.000 --> 00:00:28.000
86+
[{"metric":"cadence","value":24}]
87+
88+
00:00:28.000 --> 00:00:29.000
89+
[{"metric":"heartRate","value":110}]
90+
91+
00:00:29.000 --> 00:00:30.000
92+
[{"metric":"power","value":119}]
93+
94+
00:00:30.000 --> 00:00:31.000
95+
[{"metric":"distance","value":73.6}]
96+
97+
00:00:31.000 --> 00:00:32.000
98+
[{"metric":"cadence","value":24}]
99+
100+
00:00:32.000 --> 00:00:33.000
101+
[{"metric":"heartRate","value":110}]
102+
103+
00:00:33.000 --> 00:00:34.000
104+
[{"metric":"power","value":115}]
105+
106+
00:00:34.000 --> 00:00:35.000
107+
[{"metric":"distance","value":82.3}]
108+
109+
00:00:35.000 --> 00:00:36.000
110+
[{"metric":"cadence","value":24}]
111+
112+
00:00:36.000 --> 00:00:37.000
113+
[{"metric":"heartRate","value":110}]
114+
115+
00:00:37.000 --> 00:00:38.000
116+
[{"metric":"power","value":113}]
117+
118+
00:00:38.000 --> 00:00:39.000
119+
[{"metric":"distance","value":91}]
120+
121+
00:00:39.000 --> 00:00:40.000
122+
[{"metric":"cadence","value":24}]
123+
124+
00:00:40.000 --> 00:00:41.000
125+
[{"metric":"heartRate","value":109}]
126+
127+
00:00:41.000 --> 00:00:42.000
128+
[{"metric":"power","value":118}]
129+
130+
00:00:42.000 --> 00:00:43.000
131+
[{"metric":"distance","value":99.9}]
132+
133+
00:00:43.000 --> 00:00:44.000
134+
[{"metric":"cadence","value":24}]
135+
136+
00:00:44.000 --> 00:00:45.000
137+
[{"metric":"heartRate","value":110}]
138+
139+
00:00:45.000 --> 00:00:46.000
140+
[{"metric":"power","value":119}]
141+
142+
00:00:46.000 --> 00:00:47.000
143+
[{"metric":"distance","value":108.3}]
144+
145+
00:00:47.000 --> 00:00:48.000
146+
[{"metric":"cadence","value":23}]
147+
148+
00:00:48.000 --> 00:00:49.000
149+
[{"metric":"heartRate","value":114}]
150+
151+
00:00:49.000 --> 00:00:50.000
152+
[{"metric":"power","value":108}]
153+
154+
00:00:50.000 --> 00:00:51.000
155+
[{"metric":"distance","value":116.6}]
156+
157+
00:00:51.000 --> 00:00:52.000
158+
[{"metric":"cadence","value":24}]
159+
160+
00:00:52.000 --> 00:00:53.000
161+
[{"metric":"heartRate","value":116}]
162+
163+
00:00:53.000 --> 00:00:54.000
164+
[{"metric":"power","value":99}]

/index.m3u8

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#EXTM3U
2+
#EXT-X-VERSION:6
3+
#EXT-X-TARGETDURATION:54
4+
#EXT-X-MEDIA-SEQUENCE:0
5+
#EXT-X-PLAYLIST-TYPE:VOD
6+
#EXTINF:54.00000,
7+
fileSequence0.webvtt
8+
#EXT-X-ENDLIST
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
---
2+
status: accepted
3+
date: 2025-06-20
4+
decision-makers: @limulus
5+
---
6+
7+
# Add HLS Output Support with --hls Option
8+
9+
## Context and Problem Statement
10+
11+
Users need to output WebVTT files in HLS-compatible segmented format for HTTP Live Streaming, but tcx2webvtt currently only outputs single files to stdout. This prevents using the tool's output directly for HLS streaming scenarios where multiple segment files and a playlist are required.
12+
13+
## Decision Drivers
14+
15+
- Need for HLS streaming compatibility with Safari and HLS.js
16+
- Current stdout-only output is insufficient for multi-file HLS format requirements
17+
- Apple's `mediasubtitlesegmenter` tool (from HTTP Live Streaming Tools) fails with tcx2webvtt output due to inability to handle lines longer than ~200 characters (JSON metadata lines exceed this limit)
18+
- Requirement for directory-based output to accommodate multiple HLS segment files
19+
- Need for proper testing infrastructure for this functionality
20+
21+
## Considered Options
22+
23+
- Use `mediasubtitlesegmenter` as post-processing step
24+
- Create separate HLS conversion tool in media-tools repo
25+
- Implement custom HLS segmentation logic within tcx2webvtt
26+
27+
## Decision Outcome
28+
29+
Chosen option: "Implement custom HLS segmentation logic within tcx2webvtt", because it provides an integrated solution with proper testing infrastructure, avoids the line length limitations of `mediasubtitlesegmenter`, and feels like a natural extension of the tool's functionality.
30+
31+
### Consequences
32+
33+
- Good, because users get a complete HLS solution without needing external post-processing
34+
- Good, because we can implement proper test coverage for the HLS functionality
35+
- Good, because we avoid the line length limitations that prevent using Apple's segmentation tool
36+
- Good, because the feature feels like a natural fit for tcx2webvtt's purpose
37+
- Neutral, because this adds complexity to the tool but keeps it focused on WebVTT generation
38+
- Bad, because we need to implement and maintain custom segmentation logic instead of using existing tools
39+
40+
### Confirmation
41+
42+
Implementation compliance will be confirmed through:
43+
- Integration testing with HLS.js for browser compatibility verification
44+
- Testing with Safari and other HLS-compatible browsers
45+
- Unit tests covering segmentation logic and edge cases
46+
- Validation that generated HLS streams work correctly with seeking and playback
47+
48+
## Pros and Cons of the Options
49+
50+
### Use `mediasubtitlesegmenter` as post-processing step
51+
52+
Apple's HTTP Live Streaming Tools include `mediasubtitlesegmenter` which is designed specifically for this purpose.
53+
54+
- Good, because it uses Apple's official HLS tooling
55+
- Good, because it would require minimal changes to tcx2webvtt
56+
- Bad, because it fails with tcx2webvtt output due to JSON metadata lines exceeding ~200 character limit
57+
- Bad, because it requires users to have additional tools and processing steps
58+
59+
### Create separate HLS conversion tool in media-tools repo
60+
61+
A standalone tool could handle the conversion from tcx2webvtt output to HLS format.
62+
63+
- Good, because it separates concerns and keeps tcx2webvtt focused
64+
- Good, because media-tools repo already has HLS-related scripts
65+
- Neutral, because it would work for this specific use case
66+
- Bad, because media-tools repo lacks testing infrastructure for this type of functionality
67+
- Bad, because it creates an additional tool dependency for users
68+
- Bad, because it would likely be used only for this specific case, unlike other Apple HLS tools
69+
70+
### Implement custom HLS segmentation logic within tcx2webvtt
71+
72+
Add `--hls <directory>` option to tcx2webvtt with custom segmentation.
73+
74+
- Good, because it provides an integrated solution for users
75+
- Good, because we can implement proper test coverage
76+
- Good, because we can handle the JSON metadata line length requirements
77+
- Good, because the feature feels like a natural extension of WebVTT generation
78+
- Neutral, because it adds complexity but keeps the tool focused on its core purpose
79+
- Bad, because we need to implement and maintain custom segmentation logic
80+
81+
## More Information
82+
83+
The HLS implementation will need to:
84+
- Generate 60-second WebVTT segments with proper HLS headers
85+
- Create an `index.m3u8` playlist file
86+
- Handle empty segments appropriately
87+
- Work with Safari and HLS.js for broad compatibility
88+
- Support the existing JSON metadata format without line length restrictions

package-lock.json

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
"concurrently": "^9.1.2",
7070
"del-cli": "^6.0.0",
7171
"eslint": "^9.28.0",
72+
"hls.js": "^1.6.5",
7273
"husky": "^9.1.7",
7374
"is-ci": "^4.1.0",
7475
"mkdirp": "^3.0.1",

0 commit comments

Comments
 (0)