@bnhf/prismcast 1.3.4-2026.2.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +7 -0
- package/README.md +347 -0
- package/bin/prismcast +6 -0
- package/dist/app.d.ts +6 -0
- package/dist/app.js +315 -0
- package/dist/app.js.map +1 -0
- package/dist/browser/cdp.d.ts +38 -0
- package/dist/browser/cdp.js +155 -0
- package/dist/browser/cdp.js.map +1 -0
- package/dist/browser/channelSelection.d.ts +65 -0
- package/dist/browser/channelSelection.js +202 -0
- package/dist/browser/channelSelection.js.map +1 -0
- package/dist/browser/display.d.ts +34 -0
- package/dist/browser/display.js +54 -0
- package/dist/browser/display.js.map +1 -0
- package/dist/browser/index.d.ts +205 -0
- package/dist/browser/index.js +1205 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/tuning/fox.d.ts +2 -0
- package/dist/browser/tuning/fox.js +83 -0
- package/dist/browser/tuning/fox.js.map +1 -0
- package/dist/browser/tuning/hbo.d.ts +2 -0
- package/dist/browser/tuning/hbo.js +237 -0
- package/dist/browser/tuning/hbo.js.map +1 -0
- package/dist/browser/tuning/hulu.d.ts +2 -0
- package/dist/browser/tuning/hulu.js +550 -0
- package/dist/browser/tuning/hulu.js.map +1 -0
- package/dist/browser/tuning/sling.d.ts +2 -0
- package/dist/browser/tuning/sling.js +518 -0
- package/dist/browser/tuning/sling.js.map +1 -0
- package/dist/browser/tuning/thumbnailRow.d.ts +2 -0
- package/dist/browser/tuning/thumbnailRow.js +108 -0
- package/dist/browser/tuning/thumbnailRow.js.map +1 -0
- package/dist/browser/tuning/tileClick.d.ts +2 -0
- package/dist/browser/tuning/tileClick.js +103 -0
- package/dist/browser/tuning/tileClick.js.map +1 -0
- package/dist/browser/tuning/youtubeTv.d.ts +2 -0
- package/dist/browser/tuning/youtubeTv.js +182 -0
- package/dist/browser/tuning/youtubeTv.js.map +1 -0
- package/dist/browser/video.d.ts +289 -0
- package/dist/browser/video.js +996 -0
- package/dist/browser/video.js.map +1 -0
- package/dist/channels/index.d.ts +3 -0
- package/dist/channels/index.js +392 -0
- package/dist/channels/index.js.map +1 -0
- package/dist/config/index.d.ts +53 -0
- package/dist/config/index.js +233 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/presets.d.ts +98 -0
- package/dist/config/presets.js +241 -0
- package/dist/config/presets.js.map +1 -0
- package/dist/config/profiles.d.ts +79 -0
- package/dist/config/profiles.js +245 -0
- package/dist/config/profiles.js.map +1 -0
- package/dist/config/providers.d.ts +120 -0
- package/dist/config/providers.js +450 -0
- package/dist/config/providers.js.map +1 -0
- package/dist/config/sites.d.ts +22 -0
- package/dist/config/sites.js +377 -0
- package/dist/config/sites.js.map +1 -0
- package/dist/config/userChannels.d.ts +178 -0
- package/dist/config/userChannels.js +543 -0
- package/dist/config/userChannels.js.map +1 -0
- package/dist/config/userConfig.d.ts +235 -0
- package/dist/config/userConfig.js +913 -0
- package/dist/config/userConfig.js.map +1 -0
- package/dist/hdhr/channelMap.d.ts +21 -0
- package/dist/hdhr/channelMap.js +82 -0
- package/dist/hdhr/channelMap.js.map +1 -0
- package/dist/hdhr/deviceId.d.ts +11 -0
- package/dist/hdhr/deviceId.js +84 -0
- package/dist/hdhr/deviceId.js.map +1 -0
- package/dist/hdhr/discover.d.ts +6 -0
- package/dist/hdhr/discover.js +155 -0
- package/dist/hdhr/discover.js.map +1 -0
- package/dist/hdhr/index.d.ts +9 -0
- package/dist/hdhr/index.js +87 -0
- package/dist/hdhr/index.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +144 -0
- package/dist/index.js.map +1 -0
- package/dist/routes/assets.d.ts +6 -0
- package/dist/routes/assets.js +79 -0
- package/dist/routes/assets.js.map +1 -0
- package/dist/routes/auth.d.ts +6 -0
- package/dist/routes/auth.js +77 -0
- package/dist/routes/auth.js.map +1 -0
- package/dist/routes/channels.d.ts +6 -0
- package/dist/routes/channels.js +40 -0
- package/dist/routes/channels.js.map +1 -0
- package/dist/routes/components.d.ts +138 -0
- package/dist/routes/components.js +210 -0
- package/dist/routes/components.js.map +1 -0
- package/dist/routes/config.d.ts +72 -0
- package/dist/routes/config.js +1977 -0
- package/dist/routes/config.js.map +1 -0
- package/dist/routes/debug.d.ts +6 -0
- package/dist/routes/debug.js +274 -0
- package/dist/routes/debug.js.map +1 -0
- package/dist/routes/health.d.ts +6 -0
- package/dist/routes/health.js +85 -0
- package/dist/routes/health.js.map +1 -0
- package/dist/routes/hls.d.ts +6 -0
- package/dist/routes/hls.js +25 -0
- package/dist/routes/hls.js.map +1 -0
- package/dist/routes/index.d.ts +19 -0
- package/dist/routes/index.js +49 -0
- package/dist/routes/index.js.map +1 -0
- package/dist/routes/logs.d.ts +6 -0
- package/dist/routes/logs.js +164 -0
- package/dist/routes/logs.js.map +1 -0
- package/dist/routes/mpegts.d.ts +6 -0
- package/dist/routes/mpegts.js +19 -0
- package/dist/routes/mpegts.js.map +1 -0
- package/dist/routes/play.d.ts +6 -0
- package/dist/routes/play.js +18 -0
- package/dist/routes/play.js.map +1 -0
- package/dist/routes/playlist.d.ts +36 -0
- package/dist/routes/playlist.js +134 -0
- package/dist/routes/playlist.js.map +1 -0
- package/dist/routes/root.d.ts +6 -0
- package/dist/routes/root.js +2920 -0
- package/dist/routes/root.js.map +1 -0
- package/dist/routes/streams.d.ts +6 -0
- package/dist/routes/streams.js +88 -0
- package/dist/routes/streams.js.map +1 -0
- package/dist/routes/theme.d.ts +15 -0
- package/dist/routes/theme.js +275 -0
- package/dist/routes/theme.js.map +1 -0
- package/dist/routes/ui.d.ts +56 -0
- package/dist/routes/ui.js +354 -0
- package/dist/routes/ui.js.map +1 -0
- package/dist/service/commands.d.ts +41 -0
- package/dist/service/commands.js +391 -0
- package/dist/service/commands.js.map +1 -0
- package/dist/service/generators.d.ts +33 -0
- package/dist/service/generators.js +432 -0
- package/dist/service/generators.js.map +1 -0
- package/dist/service/index.d.ts +2 -0
- package/dist/service/index.js +7 -0
- package/dist/service/index.js.map +1 -0
- package/dist/streaming/clients.d.ts +48 -0
- package/dist/streaming/clients.js +114 -0
- package/dist/streaming/clients.js.map +1 -0
- package/dist/streaming/fmp4Segmenter.d.ts +61 -0
- package/dist/streaming/fmp4Segmenter.js +461 -0
- package/dist/streaming/fmp4Segmenter.js.map +1 -0
- package/dist/streaming/hls.d.ts +120 -0
- package/dist/streaming/hls.js +722 -0
- package/dist/streaming/hls.js.map +1 -0
- package/dist/streaming/hlsSegments.d.ts +54 -0
- package/dist/streaming/hlsSegments.js +162 -0
- package/dist/streaming/hlsSegments.js.map +1 -0
- package/dist/streaming/lifecycle.d.ts +33 -0
- package/dist/streaming/lifecycle.js +185 -0
- package/dist/streaming/lifecycle.js.map +1 -0
- package/dist/streaming/monitor.d.ts +74 -0
- package/dist/streaming/monitor.js +1310 -0
- package/dist/streaming/monitor.js.map +1 -0
- package/dist/streaming/mp4Parser.d.ts +74 -0
- package/dist/streaming/mp4Parser.js +566 -0
- package/dist/streaming/mp4Parser.js.map +1 -0
- package/dist/streaming/mpegts.d.ts +14 -0
- package/dist/streaming/mpegts.js +248 -0
- package/dist/streaming/mpegts.js.map +1 -0
- package/dist/streaming/registry.d.ts +119 -0
- package/dist/streaming/registry.js +127 -0
- package/dist/streaming/registry.js.map +1 -0
- package/dist/streaming/setup.d.ts +135 -0
- package/dist/streaming/setup.js +670 -0
- package/dist/streaming/setup.js.map +1 -0
- package/dist/streaming/showInfo.d.ts +30 -0
- package/dist/streaming/showInfo.js +362 -0
- package/dist/streaming/showInfo.js.map +1 -0
- package/dist/streaming/statusEmitter.d.ts +125 -0
- package/dist/streaming/statusEmitter.js +139 -0
- package/dist/streaming/statusEmitter.js.map +1 -0
- package/dist/types/index.d.ts +403 -0
- package/dist/types/index.js +6 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/debugFilter.d.ts +38 -0
- package/dist/utils/debugFilter.js +157 -0
- package/dist/utils/debugFilter.js.map +1 -0
- package/dist/utils/delay.d.ts +6 -0
- package/dist/utils/delay.js +15 -0
- package/dist/utils/delay.js.map +1 -0
- package/dist/utils/errors.d.ts +15 -0
- package/dist/utils/errors.js +40 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/evaluate.d.ts +51 -0
- package/dist/utils/evaluate.js +124 -0
- package/dist/utils/evaluate.js.map +1 -0
- package/dist/utils/ffmpeg.d.ts +65 -0
- package/dist/utils/ffmpeg.js +317 -0
- package/dist/utils/ffmpeg.js.map +1 -0
- package/dist/utils/fileLogger.d.ts +25 -0
- package/dist/utils/fileLogger.js +248 -0
- package/dist/utils/fileLogger.js.map +1 -0
- package/dist/utils/format.d.ts +16 -0
- package/dist/utils/format.js +46 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/html.d.ts +6 -0
- package/dist/utils/html.js +24 -0
- package/dist/utils/html.js.map +1 -0
- package/dist/utils/index.d.ts +15 -0
- package/dist/utils/index.js +20 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logEmitter.d.ts +17 -0
- package/dist/utils/logEmitter.js +30 -0
- package/dist/utils/logEmitter.js.map +1 -0
- package/dist/utils/logger.d.ts +82 -0
- package/dist/utils/logger.js +219 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/m3u.d.ts +32 -0
- package/dist/utils/m3u.js +148 -0
- package/dist/utils/m3u.js.map +1 -0
- package/dist/utils/morganStream.d.ts +7 -0
- package/dist/utils/morganStream.js +33 -0
- package/dist/utils/morganStream.js.map +1 -0
- package/dist/utils/platform.d.ts +64 -0
- package/dist/utils/platform.js +157 -0
- package/dist/utils/platform.js.map +1 -0
- package/dist/utils/retry.d.ts +15 -0
- package/dist/utils/retry.js +82 -0
- package/dist/utils/retry.js.map +1 -0
- package/dist/utils/streamContext.d.ts +28 -0
- package/dist/utils/streamContext.js +33 -0
- package/dist/utils/streamContext.js.map +1 -0
- package/dist/utils/version.d.ts +37 -0
- package/dist/utils/version.js +228 -0
- package/dist/utils/version.js.map +1 -0
- package/package.json +92 -0
- package/prismcast.png +0 -0
- package/prismcast.svg +74 -0
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
// Constants.
|
|
2
|
+
// Minimum header size: 4 bytes size + 4 bytes type.
|
|
3
|
+
const MIN_HEADER_SIZE = 8;
|
|
4
|
+
// Extended header size: 4 bytes size (==1) + 4 bytes type + 8 bytes extended size.
|
|
5
|
+
const EXTENDED_HEADER_SIZE = 16;
|
|
6
|
+
// Streaming Parser.
|
|
7
|
+
/**
|
|
8
|
+
* Creates an MP4 box parser that processes streaming input. The parser buffers incomplete boxes and invokes the callback when a complete box is available.
|
|
9
|
+
* @param onBox - Callback invoked for each complete box.
|
|
10
|
+
* @returns The parser interface with push and flush methods.
|
|
11
|
+
*/
|
|
12
|
+
export function createMP4BoxParser(onBox) {
|
|
13
|
+
// Buffer for accumulating incomplete data.
|
|
14
|
+
let buffer = Buffer.alloc(0);
|
|
15
|
+
/**
|
|
16
|
+
* Attempts to parse and emit complete boxes from the buffer.
|
|
17
|
+
*/
|
|
18
|
+
function processBuffer() {
|
|
19
|
+
// Keep parsing while we have enough data for at least a header.
|
|
20
|
+
while (buffer.length >= MIN_HEADER_SIZE) {
|
|
21
|
+
// Read the size field (first 4 bytes).
|
|
22
|
+
const sizeField = buffer.readUInt32BE(0);
|
|
23
|
+
// Determine actual box size.
|
|
24
|
+
let boxSize;
|
|
25
|
+
let headerSize;
|
|
26
|
+
if (sizeField === 1) {
|
|
27
|
+
// Extended size: need 16 bytes for full header.
|
|
28
|
+
if (buffer.length < EXTENDED_HEADER_SIZE) {
|
|
29
|
+
// Not enough data yet for extended header.
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
// Read 64-bit extended size. For practical purposes, we only use the lower 32 bits since JavaScript numbers safely handle up to 2^53, and we're unlikely to
|
|
33
|
+
// encounter boxes larger than 4GB in streaming scenarios.
|
|
34
|
+
const extendedSizeHigh = buffer.readUInt32BE(8);
|
|
35
|
+
const extendedSizeLow = buffer.readUInt32BE(12);
|
|
36
|
+
// Sanity check: reject impossibly large boxes.
|
|
37
|
+
if (extendedSizeHigh > 0) {
|
|
38
|
+
// Box claims to be > 4GB, which is unrealistic for streaming. Skip this box by advancing 1 byte and trying again.
|
|
39
|
+
buffer = buffer.subarray(1);
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
boxSize = extendedSizeLow;
|
|
43
|
+
headerSize = EXTENDED_HEADER_SIZE;
|
|
44
|
+
}
|
|
45
|
+
else if (sizeField === 0) {
|
|
46
|
+
// Size 0 means "extends to end of file" - not applicable for streaming. Skip this byte and try again.
|
|
47
|
+
buffer = buffer.subarray(1);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
boxSize = sizeField;
|
|
52
|
+
headerSize = MIN_HEADER_SIZE;
|
|
53
|
+
}
|
|
54
|
+
// Sanity check: box size must be at least the header size.
|
|
55
|
+
if (boxSize < headerSize) {
|
|
56
|
+
// Invalid box, skip one byte and try to resync.
|
|
57
|
+
buffer = buffer.subarray(1);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
// Check if we have the complete box.
|
|
61
|
+
if (buffer.length < boxSize) {
|
|
62
|
+
// Not enough data yet.
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
// Extract the complete box.
|
|
66
|
+
const boxData = buffer.subarray(0, boxSize);
|
|
67
|
+
const boxType = buffer.toString("ascii", 4, 8);
|
|
68
|
+
// Emit the box.
|
|
69
|
+
onBox({
|
|
70
|
+
data: Buffer.from(boxData),
|
|
71
|
+
size: boxSize,
|
|
72
|
+
type: boxType
|
|
73
|
+
});
|
|
74
|
+
// Advance the buffer past this box.
|
|
75
|
+
buffer = buffer.subarray(boxSize);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
flush: () => {
|
|
80
|
+
// Clear the buffer. Any remaining data is an incomplete box that we discard.
|
|
81
|
+
buffer = Buffer.alloc(0);
|
|
82
|
+
},
|
|
83
|
+
push: (chunk) => {
|
|
84
|
+
// Append the new chunk to our buffer.
|
|
85
|
+
buffer = Buffer.concat([buffer, chunk]);
|
|
86
|
+
// Try to parse complete boxes.
|
|
87
|
+
processBuffer();
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
// Nested Box Parsing.
|
|
92
|
+
/**
|
|
93
|
+
* Iterates over the immediate child boxes within a container box's payload. Container boxes in ISO 14496-12 (moof, traf, etc.) contain a sequence of child boxes
|
|
94
|
+
* starting immediately after the parent's 8-byte header. This function parses each child box header and invokes the callback with the child's type, the parent buffer,
|
|
95
|
+
* and the byte offset/size of the child box within that buffer. The callback receives offsets rather than sub-buffers to avoid memory allocation in the hot path.
|
|
96
|
+
* @param data - The complete parent box buffer including its own 8-byte header.
|
|
97
|
+
* @param callback - Called for each child box with (type, data, offset, size). The offset and size describe the child box's position within data.
|
|
98
|
+
*/
|
|
99
|
+
export function iterateChildBoxes(data, callback) {
|
|
100
|
+
let pos = MIN_HEADER_SIZE;
|
|
101
|
+
while ((pos + MIN_HEADER_SIZE) <= data.length) {
|
|
102
|
+
const sizeField = data.readUInt32BE(pos);
|
|
103
|
+
let boxSize;
|
|
104
|
+
if (sizeField === 1) {
|
|
105
|
+
// Extended size box. Need 16 bytes for the full header.
|
|
106
|
+
if ((pos + EXTENDED_HEADER_SIZE) > data.length) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
// Reject impossibly large boxes (>4GB).
|
|
110
|
+
if (data.readUInt32BE(pos + 8) > 0) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
boxSize = data.readUInt32BE(pos + 12);
|
|
114
|
+
}
|
|
115
|
+
else if ((sizeField < MIN_HEADER_SIZE) || (sizeField === 0)) {
|
|
116
|
+
// Invalid size or "extends to end of file" — stop iterating.
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
boxSize = sizeField;
|
|
121
|
+
}
|
|
122
|
+
// Ensure the child box fits within the parent.
|
|
123
|
+
if ((pos + boxSize) > data.length) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const boxType = data.toString("ascii", pos + 4, pos + 8);
|
|
127
|
+
callback(boxType, data, pos, boxSize);
|
|
128
|
+
pos += boxSize;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// Keyframe Detection.
|
|
132
|
+
/**
|
|
133
|
+
* Evaluates ISO 14496-12 sample flags to determine whether a sample is a sync sample (keyframe). The sample_depends_on field (bits 25-24) is the primary indicator,
|
|
134
|
+
* with sample_is_non_sync_sample (bit 16) as a secondary check.
|
|
135
|
+
*
|
|
136
|
+
* Sample flags layout (32 bits):
|
|
137
|
+
* - Bits 31-28: reserved
|
|
138
|
+
* - Bits 27-26: is_leading
|
|
139
|
+
* - Bits 25-24: sample_depends_on (0=unknown, 1=dependent/not keyframe, 2=independent/keyframe)
|
|
140
|
+
* - Bits 23-22: sample_is_depended_on
|
|
141
|
+
* - Bits 21-20: sample_has_redundancy
|
|
142
|
+
* - Bits 19-17: sample_padding_value
|
|
143
|
+
* - Bit 16: sample_is_non_sync_sample (0=may be sync, 1=not sync)
|
|
144
|
+
* - Bits 15-0: sample_degradation_priority
|
|
145
|
+
*
|
|
146
|
+
* @param flags - The 32-bit sample flags value.
|
|
147
|
+
* @returns true if keyframe, false if not keyframe.
|
|
148
|
+
*/
|
|
149
|
+
function evaluateSampleFlags(flags) {
|
|
150
|
+
const sampleDependsOn = (flags >>> 24) & 0x03;
|
|
151
|
+
const isNonSync = (flags >>> 16) & 0x01;
|
|
152
|
+
// sample_depends_on === 1: depends on other samples. This is not independently decodable (not a keyframe).
|
|
153
|
+
if (sampleDependsOn === 1) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
// sample_depends_on === 2: does not depend on other samples. This is an independently decodable frame (keyframe).
|
|
157
|
+
if (sampleDependsOn === 2) {
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
// sample_is_non_sync_sample === 1: explicitly marked as not a sync sample.
|
|
161
|
+
if (isNonSync === 1) {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
// sample_depends_on is unknown (0) and no non-sync marker. Per ISO 14496-12 defaults, treat as a sync sample.
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Parses a tfhd (track fragment header) box and extracts the track ID, default sample duration, and default sample flags. This is the single source of truth for
|
|
169
|
+
* tfhd field extraction, used by both detectMoofKeyframe() (for sample flags) and rewriteMoofTimestamps() (for track ID and duration).
|
|
170
|
+
*
|
|
171
|
+
* tfhd layout (FullBox):
|
|
172
|
+
* - [0-3] size, [4-7] "tfhd", [8] version, [9-11] flags, [12-15] track_ID
|
|
173
|
+
* - Optional fields (in order, each present only if its flag bit is set):
|
|
174
|
+
* 0x000001: base_data_offset (8 bytes)
|
|
175
|
+
* 0x000002: sample_description_index (4 bytes)
|
|
176
|
+
* 0x000008: default_sample_duration (4 bytes)
|
|
177
|
+
* 0x000010: default_sample_size (4 bytes)
|
|
178
|
+
* 0x000020: default_sample_flags (4 bytes)
|
|
179
|
+
*
|
|
180
|
+
* @param data - The buffer containing the tfhd box.
|
|
181
|
+
* @param offset - The byte offset of the tfhd box within the buffer.
|
|
182
|
+
* @param size - The total size of the tfhd box.
|
|
183
|
+
* @returns The parsed tfhd fields, or null if the box is malformed.
|
|
184
|
+
*/
|
|
185
|
+
function parseTfhd(data, offset, size) {
|
|
186
|
+
// Need at least the FullBox header (12 bytes) plus track_ID (4 bytes) = 16 bytes.
|
|
187
|
+
if (size < 16) {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
const tfhdFlags = data.readUInt32BE(offset + 8) & 0x00FFFFFF;
|
|
191
|
+
const trackId = data.readUInt32BE(offset + 12);
|
|
192
|
+
// Walk past optional fields in order. Each field is present only if its corresponding flag bit is set.
|
|
193
|
+
let pos = offset + 16;
|
|
194
|
+
if (tfhdFlags & 0x000001) {
|
|
195
|
+
pos += 8;
|
|
196
|
+
}
|
|
197
|
+
if (tfhdFlags & 0x000002) {
|
|
198
|
+
pos += 4;
|
|
199
|
+
}
|
|
200
|
+
// Extract default_sample_duration if present.
|
|
201
|
+
let defaultSampleDuration = 0;
|
|
202
|
+
if (tfhdFlags & 0x000008) {
|
|
203
|
+
if ((pos + 4) > (offset + size)) {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
defaultSampleDuration = data.readUInt32BE(pos);
|
|
207
|
+
pos += 4;
|
|
208
|
+
}
|
|
209
|
+
// Skip default_sample_size.
|
|
210
|
+
if (tfhdFlags & 0x000010) {
|
|
211
|
+
pos += 4;
|
|
212
|
+
}
|
|
213
|
+
// Extract default_sample_flags if present.
|
|
214
|
+
let defaultSampleFlags = null;
|
|
215
|
+
if (tfhdFlags & 0x000020) {
|
|
216
|
+
if ((pos + 4) > (offset + size)) {
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
defaultSampleFlags = data.readUInt32BE(pos);
|
|
220
|
+
}
|
|
221
|
+
return { defaultSampleDuration, defaultSampleFlags, trackId };
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Extracts the sample flags for the first sample in a trun (track fragment run) box. The flags are resolved from three sources in priority order:
|
|
225
|
+
*
|
|
226
|
+
* 1. first_sample_flags field in the trun (trun flags bit 0x004) — explicitly overrides the first sample's flags.
|
|
227
|
+
* 2. Per-sample flags from the first sample entry (trun flags bit 0x400) — individual sample flags are present in each entry.
|
|
228
|
+
* 3. default_sample_flags from the parent tfhd — applies when neither first_sample_flags nor per-sample flags are available.
|
|
229
|
+
*
|
|
230
|
+
* trun layout (FullBox):
|
|
231
|
+
* - [0-3] size, [4-7] "trun", [8] version, [9-11] flags, [12-15] sample_count
|
|
232
|
+
* - Optional fields after sample_count:
|
|
233
|
+
* 0x001: data_offset (4 bytes)
|
|
234
|
+
* 0x004: first_sample_flags (4 bytes)
|
|
235
|
+
* - Per-sample entries (each containing optional fields based on flags):
|
|
236
|
+
* 0x100: sample_duration (4 bytes)
|
|
237
|
+
* 0x200: sample_size (4 bytes)
|
|
238
|
+
* 0x400: sample_flags (4 bytes)
|
|
239
|
+
* 0x800: sample_composition_time_offset (4 bytes)
|
|
240
|
+
*
|
|
241
|
+
* @param data - The buffer containing the trun box.
|
|
242
|
+
* @param offset - The byte offset of the trun box within the buffer.
|
|
243
|
+
* @param size - The total size of the trun box.
|
|
244
|
+
* @param defaultSampleFlags - The default_sample_flags from the parent tfhd, or null if not present.
|
|
245
|
+
* @returns The resolved sample flags for the first sample, or null if no source is available.
|
|
246
|
+
*/
|
|
247
|
+
function extractFirstSampleFlags(data, offset, size, defaultSampleFlags) {
|
|
248
|
+
// Need at least the FullBox header (12 bytes) plus sample_count (4 bytes) = 16 bytes.
|
|
249
|
+
if (size < 16) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
const trunFlags = data.readUInt32BE(offset + 8) & 0x00FFFFFF;
|
|
253
|
+
const sampleCount = data.readUInt32BE(offset + 12);
|
|
254
|
+
// No samples means no flags to extract.
|
|
255
|
+
if (sampleCount === 0) {
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
let pos = offset + 16;
|
|
259
|
+
// Skip optional data_offset field.
|
|
260
|
+
if (trunFlags & 0x001) {
|
|
261
|
+
pos += 4;
|
|
262
|
+
}
|
|
263
|
+
// Primary source: first_sample_flags field overrides the first sample's flags when present.
|
|
264
|
+
if (trunFlags & 0x004) {
|
|
265
|
+
if ((pos + 4) > (offset + size)) {
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
return data.readUInt32BE(pos);
|
|
269
|
+
}
|
|
270
|
+
// Secondary source: per-sample flags from the first sample entry. The per-sample entry fields appear in order: duration (0x100), size (0x200), flags (0x400),
|
|
271
|
+
// composition_time_offset (0x800). We skip duration and size to reach the flags field of the first entry.
|
|
272
|
+
if (trunFlags & 0x400) {
|
|
273
|
+
// Skip first_sample_flags field position (it's not present since we checked 0x004 above, but the pos is already past it).
|
|
274
|
+
if (trunFlags & 0x100) {
|
|
275
|
+
pos += 4;
|
|
276
|
+
}
|
|
277
|
+
if (trunFlags & 0x200) {
|
|
278
|
+
pos += 4;
|
|
279
|
+
}
|
|
280
|
+
if ((pos + 4) > (offset + size)) {
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
return data.readUInt32BE(pos);
|
|
284
|
+
}
|
|
285
|
+
// Tertiary source: default_sample_flags from the parent tfhd.
|
|
286
|
+
return defaultSampleFlags;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Detects whether a moof box starts with a keyframe (sync sample) by examining the sample flags of the first sample in each trun box. The detection inspects all traf
|
|
290
|
+
* boxes within the moof to handle multi-track containers (e.g., separate audio and video tracks). A non-keyframe signal from any traf (sample_depends_on === 2) takes
|
|
291
|
+
* precedence because audio tracks are always independently decodable — the only source of sample_depends_on === 2 is a non-keyframe video track. This avoids needing
|
|
292
|
+
* to map track IDs back to the moov box's codec metadata.
|
|
293
|
+
*
|
|
294
|
+
* The function checks three flag sources in priority order per the ISO 14496-12 spec: trun first_sample_flags (0x004), trun per-sample flags (0x400), and tfhd
|
|
295
|
+
* default_sample_flags (0x020).
|
|
296
|
+
*
|
|
297
|
+
* @param moofData - The complete moof box buffer including its 8-byte header.
|
|
298
|
+
* @returns true if the moof starts with a keyframe, false if it starts with a non-keyframe, or null if the flags could not be determined.
|
|
299
|
+
*/
|
|
300
|
+
export function detectMoofKeyframe(moofData) {
|
|
301
|
+
let hasExplicitKeyframe = false;
|
|
302
|
+
let hasExplicitNonKeyframe = false;
|
|
303
|
+
// Walk the moof's child boxes looking for traf (track fragment) boxes.
|
|
304
|
+
iterateChildBoxes(moofData, (type, data, offset, size) => {
|
|
305
|
+
if (type !== "traf") {
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
// Create a subarray for this traf so we can iterate its child boxes. Buffer.subarray() shares memory with the parent, so this is O(1) with no data copying.
|
|
309
|
+
const trafData = data.subarray(offset, offset + size);
|
|
310
|
+
let defaultSampleFlags = null;
|
|
311
|
+
// Walk the traf's child boxes. We need tfhd for default_sample_flags (fallback) and trun for the actual first-sample flags. tfhd always precedes trun in the
|
|
312
|
+
// spec-mandated box ordering, so defaultSampleFlags will be populated before any trun is processed.
|
|
313
|
+
iterateChildBoxes(trafData, (childType, childData, childOffset, childSize) => {
|
|
314
|
+
if (childType === "tfhd") {
|
|
315
|
+
defaultSampleFlags = parseTfhd(childData, childOffset, childSize)?.defaultSampleFlags ?? null;
|
|
316
|
+
}
|
|
317
|
+
else if (childType === "trun") {
|
|
318
|
+
const sampleFlags = extractFirstSampleFlags(childData, childOffset, childSize, defaultSampleFlags);
|
|
319
|
+
if (sampleFlags !== null) {
|
|
320
|
+
const isKeyframe = evaluateSampleFlags(sampleFlags);
|
|
321
|
+
if (isKeyframe) {
|
|
322
|
+
hasExplicitKeyframe = true;
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
hasExplicitNonKeyframe = true;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
// A non-keyframe traf (video track with sample_depends_on === 2) overrides keyframe trafs. Audio tracks are always sync (sample_depends_on 0 or 1), so the presence
|
|
332
|
+
// of any non-keyframe signal is the definitive indicator that this fragment does not start with a video keyframe. TypeScript's control flow analysis cannot track
|
|
333
|
+
// mutations made inside the iterateChildBoxes callback, so these variables appear "always falsy" to the linter despite being set to true at runtime.
|
|
334
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
335
|
+
if (hasExplicitNonKeyframe) {
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
339
|
+
if (hasExplicitKeyframe) {
|
|
340
|
+
return true;
|
|
341
|
+
}
|
|
342
|
+
// No definitive signal from any traf.
|
|
343
|
+
return null;
|
|
344
|
+
}
|
|
345
|
+
// Timestamp Rewriting.
|
|
346
|
+
/**
|
|
347
|
+
* Computes the total duration of all samples in a trun (track fragment run) box. The duration is the sum of per-sample durations when present (trun flags bit 0x100),
|
|
348
|
+
* or sampleCount * defaultSampleDuration as a fallback. The result is returned as a BigInt for safe accumulation into the 64-bit timestamp counter.
|
|
349
|
+
*
|
|
350
|
+
* trun layout (FullBox):
|
|
351
|
+
* - [0-3] size, [4-7] "trun", [8] version, [9-11] flags, [12-15] sample_count
|
|
352
|
+
* - Optional fields after sample_count:
|
|
353
|
+
* 0x001: data_offset (4 bytes)
|
|
354
|
+
* 0x004: first_sample_flags (4 bytes)
|
|
355
|
+
* - Per-sample entries (each containing optional fields based on flags):
|
|
356
|
+
* 0x100: sample_duration (4 bytes)
|
|
357
|
+
* 0x200: sample_size (4 bytes)
|
|
358
|
+
* 0x400: sample_flags (4 bytes)
|
|
359
|
+
* 0x800: sample_composition_time_offset (4 bytes)
|
|
360
|
+
*
|
|
361
|
+
* @param data - The buffer containing the trun box.
|
|
362
|
+
* @param offset - The byte offset of the trun box within the buffer.
|
|
363
|
+
* @param size - The total size of the trun box.
|
|
364
|
+
* @param defaultSampleDuration - The default sample duration from the parent tfhd, used when per-sample durations are absent.
|
|
365
|
+
* @returns The total duration of all samples as a BigInt, or 0n if the box is malformed or empty.
|
|
366
|
+
*/
|
|
367
|
+
function extractTrunTotalDuration(data, offset, size, defaultSampleDuration) {
|
|
368
|
+
// Need at least the FullBox header (12 bytes) plus sample_count (4 bytes) = 16 bytes.
|
|
369
|
+
if (size < 16) {
|
|
370
|
+
return 0n;
|
|
371
|
+
}
|
|
372
|
+
const trunFlags = data.readUInt32BE(offset + 8) & 0x00FFFFFF;
|
|
373
|
+
const sampleCount = data.readUInt32BE(offset + 12);
|
|
374
|
+
if (sampleCount === 0) {
|
|
375
|
+
return 0n;
|
|
376
|
+
}
|
|
377
|
+
// If per-sample durations are not present, use defaultSampleDuration * sampleCount.
|
|
378
|
+
if (!(trunFlags & 0x100)) {
|
|
379
|
+
return BigInt(defaultSampleDuration) * BigInt(sampleCount);
|
|
380
|
+
}
|
|
381
|
+
// Compute the byte size of each per-sample entry from the trun flags. Each optional field adds 4 bytes to the entry. The duration field (0x100) is always present
|
|
382
|
+
// here because we returned early above when it was not set.
|
|
383
|
+
let entrySize = 4;
|
|
384
|
+
if (trunFlags & 0x200) {
|
|
385
|
+
entrySize += 4;
|
|
386
|
+
}
|
|
387
|
+
if (trunFlags & 0x400) {
|
|
388
|
+
entrySize += 4;
|
|
389
|
+
}
|
|
390
|
+
if (trunFlags & 0x800) {
|
|
391
|
+
entrySize += 4;
|
|
392
|
+
}
|
|
393
|
+
// Walk past the optional header fields to reach the sample entries.
|
|
394
|
+
let pos = offset + 16;
|
|
395
|
+
if (trunFlags & 0x001) {
|
|
396
|
+
pos += 4;
|
|
397
|
+
}
|
|
398
|
+
if (trunFlags & 0x004) {
|
|
399
|
+
pos += 4;
|
|
400
|
+
}
|
|
401
|
+
// Sum per-sample durations. The duration field is the first field in each entry (when present), since entry fields appear in order: duration, size, flags,
|
|
402
|
+
// composition_time_offset.
|
|
403
|
+
let totalDuration = 0n;
|
|
404
|
+
const endPos = offset + size;
|
|
405
|
+
for (let i = 0; i < sampleCount; i++) {
|
|
406
|
+
if ((pos + 4) > endPos) {
|
|
407
|
+
break;
|
|
408
|
+
}
|
|
409
|
+
totalDuration += BigInt(data.readUInt32BE(pos));
|
|
410
|
+
pos += entrySize;
|
|
411
|
+
}
|
|
412
|
+
return totalDuration;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Rewrites the baseMediaDecodeTime in every tfdt box within a moof to produce monotonically increasing timestamps. For each traf, the function reads the track ID from
|
|
416
|
+
* tfhd, writes the current per-track timestamp into tfdt, then advances the counter by the total sample duration extracted from trun. This eliminates timestamp
|
|
417
|
+
* discontinuities caused by capture restarts (tab replacement, source reload recovery) because Chrome's original timestamps are completely overwritten.
|
|
418
|
+
*
|
|
419
|
+
* The rewrite is done in-place on the moof buffer. This is safe because the buffer is an owned copy created by the MP4 box parser (Buffer.from() in createMP4BoxParser).
|
|
420
|
+
*
|
|
421
|
+
* Per-track state is necessary because FFmpeg outputs separate moof+mdat pairs for audio and video, and each track may have a different timescale (e.g., 90000 for
|
|
422
|
+
* video, 48000 for audio). A single counter would produce incorrect timestamps for one track.
|
|
423
|
+
*
|
|
424
|
+
* @param moofData - The complete moof box buffer including its 8-byte header. Modified in place.
|
|
425
|
+
* @param trackTimestamps - Map from track_ID to the next baseMediaDecodeTime value. Updated in place with the advanced timestamps after rewriting.
|
|
426
|
+
* @returns Map from track_ID to the total duration (in timescale units) computed from trun sample durations. The caller uses this for diagnostics (zero durations
|
|
427
|
+
* indicate trun parsing issues) and sanity checking (abnormal durations from corrupt trun data). Each entry corresponds to one traf box in the moof.
|
|
428
|
+
*/
|
|
429
|
+
export function rewriteMoofTimestamps(moofData, trackTimestamps) {
|
|
430
|
+
const trafDurations = new Map();
|
|
431
|
+
// Walk the moof's child boxes looking for traf (track fragment) boxes.
|
|
432
|
+
iterateChildBoxes(moofData, (type, data, offset, size) => {
|
|
433
|
+
if (type !== "traf") {
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
// Create a subarray for this traf so we can iterate its child boxes. Buffer.subarray() shares memory with the parent, so this is O(1) with no data copying.
|
|
437
|
+
const trafData = data.subarray(offset, offset + size);
|
|
438
|
+
let tfhdInfo = null;
|
|
439
|
+
let totalDuration = 0n;
|
|
440
|
+
// Walk the traf's child boxes. The spec mandates tfhd before tfdt before trun, so we process them in order. The first pass extracts tfhd info and rewrites
|
|
441
|
+
// tfdt. The trun duration extraction happens in the same pass since tfhd is already available by the time trun is encountered.
|
|
442
|
+
iterateChildBoxes(trafData, (childType, childData, childOffset, childSize) => {
|
|
443
|
+
if (childType === "tfhd") {
|
|
444
|
+
tfhdInfo = parseTfhd(childData, childOffset, childSize);
|
|
445
|
+
}
|
|
446
|
+
else if (childType === "tfdt") {
|
|
447
|
+
// Rewrite baseMediaDecodeTime with the current per-track timestamp. The tfhd must precede tfdt per the ISO 14496-12 box ordering, so tfhdInfo is
|
|
448
|
+
// available here.
|
|
449
|
+
if (!tfhdInfo) {
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
const currentTimestamp = trackTimestamps.get(tfhdInfo.trackId) ?? 0n;
|
|
453
|
+
// tfdt layout (FullBox): [0-3] size, [4-7] "tfdt", [8] version, [9-11] flags, [12+] baseMediaDecodeTime.
|
|
454
|
+
// Version 0: 32-bit baseMediaDecodeTime at offset 12. Version 1: 64-bit baseMediaDecodeTime at offset 12.
|
|
455
|
+
if (childSize < 16) {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
const version = childData.readUInt8(childOffset + 8);
|
|
459
|
+
if (version === 0) {
|
|
460
|
+
// Version 0: write as uint32 (truncate to 32 bits). Overflows at ~13 hours for 90000 Hz timescale, but version 0 tfdt boxes are inherently 32-bit.
|
|
461
|
+
childData.writeUInt32BE(Number(currentTimestamp & 0xffffffffn), childOffset + 12);
|
|
462
|
+
}
|
|
463
|
+
else {
|
|
464
|
+
// Version 1: write as uint64 (high 32 bits + low 32 bits). Requires at least 20 bytes in the tfdt box.
|
|
465
|
+
if (childSize < 20) {
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
const high = Number((currentTimestamp >> 32n) & 0xffffffffn);
|
|
469
|
+
const low = Number(currentTimestamp & 0xffffffffn);
|
|
470
|
+
childData.writeUInt32BE(high, childOffset + 12);
|
|
471
|
+
childData.writeUInt32BE(low, childOffset + 16);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
else if (childType === "trun") {
|
|
475
|
+
// Accumulate sample durations from each trun. A traf can contain multiple trun boxes (though rare in practice). The tfhd must precede trun per spec
|
|
476
|
+
// ordering, so tfhdInfo and its defaultSampleDuration are available here.
|
|
477
|
+
if (tfhdInfo) {
|
|
478
|
+
totalDuration += extractTrunTotalDuration(childData, childOffset, childSize, tfhdInfo.defaultSampleDuration);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
// Advance the per-track timestamp counter by the total sample duration from this traf. A zero duration with a valid tfhd indicates a trun parsing issue —
|
|
483
|
+
// the caller should log a warning. We still advance (by zero) to avoid corrupting subsequent timestamps. TypeScript's control flow analysis cannot track
|
|
484
|
+
// mutations made inside the iterateChildBoxes callback, so tfhdInfo appears "always null" to the linter despite being set at runtime.
|
|
485
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
486
|
+
if (tfhdInfo) {
|
|
487
|
+
const info = tfhdInfo;
|
|
488
|
+
trafDurations.set(info.trackId, totalDuration);
|
|
489
|
+
const currentTimestamp = trackTimestamps.get(info.trackId) ?? 0n;
|
|
490
|
+
trackTimestamps.set(info.trackId, currentTimestamp + totalDuration);
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
return trafDurations;
|
|
494
|
+
}
|
|
495
|
+
// Moov Timescale Extraction.
|
|
496
|
+
/**
|
|
497
|
+
* Extracts per-track timescale values from a moov (movie header) box. Each track in the moov contains a tkhd box with the track_ID and an mdia > mdhd box with the
|
|
498
|
+
* timescale. The timescale converts sample durations (in timescale units) to real seconds: seconds = duration / timescale. For example, a timescale of 16000 means
|
|
499
|
+
* each unit is 1/16000 of a second.
|
|
500
|
+
*
|
|
501
|
+
* Parsing path: moov > trak > { tkhd (track_ID), mdia > mdhd (timescale) }
|
|
502
|
+
*
|
|
503
|
+
* @param moovData - The complete moov box buffer including its 8-byte header.
|
|
504
|
+
* @returns Map from track_ID to timescale. Empty if no valid tracks are found.
|
|
505
|
+
*/
|
|
506
|
+
export function parseMoovTimescales(moovData) {
|
|
507
|
+
const result = new Map();
|
|
508
|
+
// Walk the moov's child boxes looking for trak (track) boxes.
|
|
509
|
+
iterateChildBoxes(moovData, (type, data, offset, size) => {
|
|
510
|
+
if (type !== "trak") {
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
const trakData = data.subarray(offset, offset + size);
|
|
514
|
+
let trackId = null;
|
|
515
|
+
let timescale = null;
|
|
516
|
+
// Walk the trak's child boxes to find tkhd (track header) and mdia (media container). The spec mandates tkhd before mdia, so trackId is available before we
|
|
517
|
+
// need it, but we don't depend on ordering — both are extracted independently and combined after iteration.
|
|
518
|
+
iterateChildBoxes(trakData, (childType, childData, childOffset, childSize) => {
|
|
519
|
+
if (childType === "tkhd") {
|
|
520
|
+
// tkhd layout (FullBox): [0-3] size, [4-7] "tkhd", [8] version, [9-11] flags.
|
|
521
|
+
// Version 0: [12-15] creation_time, [16-19] modification_time, [20-23] track_ID.
|
|
522
|
+
// Version 1: [12-19] creation_time, [20-27] modification_time, [28-31] track_ID.
|
|
523
|
+
if (childSize < 16) {
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
const version = childData.readUInt8(childOffset + 8);
|
|
527
|
+
if ((version === 0) && (childSize >= 24)) {
|
|
528
|
+
trackId = childData.readUInt32BE(childOffset + 20);
|
|
529
|
+
}
|
|
530
|
+
else if ((version === 1) && (childSize >= 32)) {
|
|
531
|
+
trackId = childData.readUInt32BE(childOffset + 28);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
else if (childType === "mdia") {
|
|
535
|
+
// Walk the mdia's child boxes to find mdhd (media header) which contains the timescale.
|
|
536
|
+
const mdiaData = childData.subarray(childOffset, childOffset + childSize);
|
|
537
|
+
iterateChildBoxes(mdiaData, (mdiaChildType, mdiaChildData, mdiaChildOffset, mdiaChildSize) => {
|
|
538
|
+
if (mdiaChildType !== "mdhd") {
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
// mdhd layout (FullBox): [0-3] size, [4-7] "mdhd", [8] version, [9-11] flags.
|
|
542
|
+
// Version 0: [12-15] creation_time, [16-19] modification_time, [20-23] timescale.
|
|
543
|
+
// Version 1: [12-19] creation_time, [20-27] modification_time, [28-31] timescale.
|
|
544
|
+
if (mdiaChildSize < 16) {
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
const mdhdVersion = mdiaChildData.readUInt8(mdiaChildOffset + 8);
|
|
548
|
+
if ((mdhdVersion === 0) && (mdiaChildSize >= 24)) {
|
|
549
|
+
timescale = mdiaChildData.readUInt32BE(mdiaChildOffset + 20);
|
|
550
|
+
}
|
|
551
|
+
else if ((mdhdVersion === 1) && (mdiaChildSize >= 32)) {
|
|
552
|
+
timescale = mdiaChildData.readUInt32BE(mdiaChildOffset + 28);
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
});
|
|
557
|
+
// Store the track if both trackId and timescale were successfully extracted. TypeScript's control flow analysis cannot track mutations made inside the
|
|
558
|
+
// iterateChildBoxes callbacks, so these variables appear "always null" to the linter despite being set at runtime.
|
|
559
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
560
|
+
if ((trackId !== null) && (timescale !== null) && (timescale > 0)) {
|
|
561
|
+
result.set(trackId, timescale);
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
return result;
|
|
565
|
+
}
|
|
566
|
+
//# sourceMappingURL=mp4Parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mp4Parser.js","sourceRoot":"","sources":["../../src/streaming/mp4Parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Request, Response } from "express";
|
|
2
|
+
/**
|
|
3
|
+
* Handles MPEG-TS stream requests. Validates the channel, flushes HTTP headers early for new streams, then ensures a capture is running, waits for the init segment,
|
|
4
|
+
* spawns a per-client FFmpeg remuxer, and streams the output.
|
|
5
|
+
*
|
|
6
|
+
* For new streams, headers are flushed before stream setup begins so the client sees an immediate 200 response. This prevents timeout failures during the 4-10+
|
|
7
|
+
* second startup sequence. The trade-off is that error responses cannot be sent after the flush — failures are logged server-side and the connection is closed.
|
|
8
|
+
*
|
|
9
|
+
* Route: GET /stream/:name
|
|
10
|
+
*
|
|
11
|
+
* @param req - Express request object.
|
|
12
|
+
* @param res - Express response object.
|
|
13
|
+
*/
|
|
14
|
+
export declare function handleMpegTsStream(req: Request, res: Response): Promise<void>;
|