@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,61 @@
|
|
|
1
|
+
import type { Nullable } from "../types/index.js";
|
|
2
|
+
import type { Readable } from "node:stream";
|
|
3
|
+
/**
|
|
4
|
+
* Options for creating an fMP4 segmenter.
|
|
5
|
+
*/
|
|
6
|
+
export interface FMP4SegmenterOptions {
|
|
7
|
+
initialTrackTimestamps?: Map<number, bigint>;
|
|
8
|
+
onError: (error: Error) => void;
|
|
9
|
+
onStop: () => void;
|
|
10
|
+
pendingDiscontinuity?: boolean;
|
|
11
|
+
previousInitSegment?: Nullable<Buffer>;
|
|
12
|
+
startingInitVersion?: number;
|
|
13
|
+
startingSegmentIndex?: number;
|
|
14
|
+
streamId: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Keyframe detection statistics tracked across the lifetime of a segmenter. These metrics provide visibility into the actual keyframe frequency in the fMP4 output,
|
|
18
|
+
* which is critical for diagnosing frozen screen issues in downstream consumers like Channels DVR.
|
|
19
|
+
*/
|
|
20
|
+
export interface KeyframeStats {
|
|
21
|
+
averageKeyframeIntervalMs: number;
|
|
22
|
+
indeterminateCount: number;
|
|
23
|
+
keyframeCount: number;
|
|
24
|
+
maxKeyframeIntervalMs: number;
|
|
25
|
+
minKeyframeIntervalMs: number;
|
|
26
|
+
nonKeyframeCount: number;
|
|
27
|
+
segmentsWithoutLeadingKeyframe: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Result of creating an fMP4 segmenter.
|
|
31
|
+
*/
|
|
32
|
+
export interface FMP4SegmenterResult {
|
|
33
|
+
getInitSegment: () => Nullable<Buffer>;
|
|
34
|
+
getInitVersion: () => number;
|
|
35
|
+
getKeyframeStats: () => KeyframeStats;
|
|
36
|
+
getLastSegmentSize: () => number;
|
|
37
|
+
getSegmentIndex: () => number;
|
|
38
|
+
getTrackTimestamps: () => Map<number, bigint>;
|
|
39
|
+
markDiscontinuity: () => void;
|
|
40
|
+
pipe: (stream: Readable) => void;
|
|
41
|
+
stop: () => void;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Formats keyframe statistics into a human-readable summary for the termination log. Returns an empty string if no moof boxes were processed. The format mirrors the
|
|
45
|
+
* recovery metrics summary style used in monitor.ts.
|
|
46
|
+
*
|
|
47
|
+
* Example output:
|
|
48
|
+
* - "Keyframes: 2490 of 2490 moofs (100.0%), interval 1.9-2.1s avg 2.0s."
|
|
49
|
+
* - "Keyframes: 85 of 198 moofs (42.9%), interval 1.8-12.4s avg 3.1s, 5 segments without leading keyframe."
|
|
50
|
+
*
|
|
51
|
+
* @param stats - The keyframe statistics to format.
|
|
52
|
+
* @returns Formatted summary string, or empty string if no data.
|
|
53
|
+
*/
|
|
54
|
+
export declare function formatKeyframeStatsSummary(stats: KeyframeStats): string;
|
|
55
|
+
/**
|
|
56
|
+
* Creates an fMP4 segmenter that transforms MP4 input into HLS segments. The segmenter parses MP4 boxes, extracts the init segment, detects keyframes in each moof
|
|
57
|
+
* fragment, and accumulates media fragments into segments based on the configured duration.
|
|
58
|
+
* @param options - Segmenter options including stream ID and callbacks.
|
|
59
|
+
* @returns The segmenter interface with pipe, stop, and keyframe stats methods.
|
|
60
|
+
*/
|
|
61
|
+
export declare function createFMP4Segmenter(options: FMP4SegmenterOptions): FMP4SegmenterResult;
|
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
/* Copyright(C) 2024-2026, HJD (https://github.com/hjdhjd). All rights reserved.
|
|
2
|
+
*
|
|
3
|
+
* fmp4Segmenter.ts: fMP4 HLS segmentation for PrismCast.
|
|
4
|
+
*/
|
|
5
|
+
import { createMP4BoxParser, detectMoofKeyframe, parseMoovTimescales, rewriteMoofTimestamps } from "./mp4Parser.js";
|
|
6
|
+
import { storeInitSegment, storeSegment, updatePlaylist } from "./hlsSegments.js";
|
|
7
|
+
import { CONFIG } from "../config/index.js";
|
|
8
|
+
import { LOG } from "../utils/index.js";
|
|
9
|
+
/* This module transforms a puppeteer-stream MP4 capture into HLS fMP4 segments. The overall flow is: (1) receive MP4 data from puppeteer-stream (H.264 + AAC from
|
|
10
|
+
* either native capture or FFmpeg transcoding), (2) parse MP4 box structure to identify ftyp + moov (initialization segment) and moof + mdat pairs (media fragments),
|
|
11
|
+
* (3) store init segment and accumulate media fragments into segments, and (4) generate and update the m3u8 playlist.
|
|
12
|
+
*
|
|
13
|
+
* Keyframe detection is available for diagnostics by setting KEYFRAME_DEBUG to true. When enabled, each moof's traf/trun sample flags are parsed (ISO 14496-12) to
|
|
14
|
+
* determine whether fragments start with sync samples (keyframes). Statistics are logged at stream termination and per-segment warnings are emitted for segments that
|
|
15
|
+
* don't start with a keyframe. When disabled, the moof data is passed through without inspection.
|
|
16
|
+
*/
|
|
17
|
+
// Set to true to enable keyframe detection and statistics. This parses traf/trun sample flags in each moof to track keyframe frequency and log per-segment warnings.
|
|
18
|
+
// Useful for diagnosing frozen screen issues in downstream HLS consumers.
|
|
19
|
+
const KEYFRAME_DEBUG = false;
|
|
20
|
+
// Ratio threshold for the duration sanity check. If a trun's computed total duration exceeds this multiple of the established per-track baseline (or falls below 1/Nth),
|
|
21
|
+
// the duration is considered corrupt and the timestamp counter is corrected to use the baseline value instead. A factor of 20 is deliberately generous — Chrome's
|
|
22
|
+
// MediaRecorder legitimately produces bursty moofs (5-8x normal) when the source player buffers and then delivers accumulated frames in a burst. Those are real data,
|
|
23
|
+
// not corruption. This ratio catches only truly pathological values while letting burst moofs pass through with their correct durations.
|
|
24
|
+
const DURATION_SANITY_RATIO = 20n;
|
|
25
|
+
// Keyframe Stats Formatting.
|
|
26
|
+
/**
|
|
27
|
+
* Formats keyframe statistics into a human-readable summary for the termination log. Returns an empty string if no moof boxes were processed. The format mirrors the
|
|
28
|
+
* recovery metrics summary style used in monitor.ts.
|
|
29
|
+
*
|
|
30
|
+
* Example output:
|
|
31
|
+
* - "Keyframes: 2490 of 2490 moofs (100.0%), interval 1.9-2.1s avg 2.0s."
|
|
32
|
+
* - "Keyframes: 85 of 198 moofs (42.9%), interval 1.8-12.4s avg 3.1s, 5 segments without leading keyframe."
|
|
33
|
+
*
|
|
34
|
+
* @param stats - The keyframe statistics to format.
|
|
35
|
+
* @returns Formatted summary string, or empty string if no data.
|
|
36
|
+
*/
|
|
37
|
+
export function formatKeyframeStatsSummary(stats) {
|
|
38
|
+
const totalMoofs = stats.keyframeCount + stats.nonKeyframeCount + stats.indeterminateCount;
|
|
39
|
+
// No moof boxes were processed — stream ended before any media fragments arrived.
|
|
40
|
+
if (totalMoofs === 0) {
|
|
41
|
+
return "";
|
|
42
|
+
}
|
|
43
|
+
const percentage = ((stats.keyframeCount / totalMoofs) * 100).toFixed(1);
|
|
44
|
+
const parts = ["Keyframes: ", String(stats.keyframeCount), " of ", String(totalMoofs), " moofs (", percentage, "%)"];
|
|
45
|
+
// Include interval statistics if we have at least two keyframes (needed for a meaningful interval).
|
|
46
|
+
if (stats.keyframeCount >= 2) {
|
|
47
|
+
const minSec = (stats.minKeyframeIntervalMs / 1000).toFixed(1);
|
|
48
|
+
const maxSec = (stats.maxKeyframeIntervalMs / 1000).toFixed(1);
|
|
49
|
+
const avgSec = (stats.averageKeyframeIntervalMs / 1000).toFixed(1);
|
|
50
|
+
parts.push(", interval ", minSec, "-", maxSec, "s avg ", avgSec, "s");
|
|
51
|
+
}
|
|
52
|
+
// Note segments that didn't start with a keyframe — these directly correlate with potential frozen frame issues.
|
|
53
|
+
if (stats.segmentsWithoutLeadingKeyframe > 0) {
|
|
54
|
+
parts.push(", ", String(stats.segmentsWithoutLeadingKeyframe), " segment");
|
|
55
|
+
if (stats.segmentsWithoutLeadingKeyframe !== 1) {
|
|
56
|
+
parts.push("s");
|
|
57
|
+
}
|
|
58
|
+
parts.push(" without leading keyframe");
|
|
59
|
+
}
|
|
60
|
+
parts.push(".");
|
|
61
|
+
return parts.join("");
|
|
62
|
+
}
|
|
63
|
+
// Segmenter Implementation.
|
|
64
|
+
/**
|
|
65
|
+
* Creates an fMP4 segmenter that transforms MP4 input into HLS segments. The segmenter parses MP4 boxes, extracts the init segment, detects keyframes in each moof
|
|
66
|
+
* fragment, and accumulates media fragments into segments based on the configured duration.
|
|
67
|
+
* @param options - Segmenter options including stream ID and callbacks.
|
|
68
|
+
* @returns The segmenter interface with pipe, stop, and keyframe stats methods.
|
|
69
|
+
*/
|
|
70
|
+
export function createFMP4Segmenter(options) {
|
|
71
|
+
const { initialTrackTimestamps, onError, onStop, pendingDiscontinuity, previousInitSegment, startingInitVersion, startingSegmentIndex, streamId } = options;
|
|
72
|
+
// Initialize state.
|
|
73
|
+
const state = {
|
|
74
|
+
discontinuityIndices: new Set(),
|
|
75
|
+
firstSegmentEmitted: false,
|
|
76
|
+
fragmentBuffer: [],
|
|
77
|
+
hasInit: false,
|
|
78
|
+
indeterminateCount: 0,
|
|
79
|
+
initBoxes: [],
|
|
80
|
+
initSegment: null,
|
|
81
|
+
initVersion: startingInitVersion ?? 0,
|
|
82
|
+
keyframeCount: 0,
|
|
83
|
+
lastKeyframeTime: null,
|
|
84
|
+
lastSegmentSize: 0,
|
|
85
|
+
maxKeyframeIntervalMs: 0,
|
|
86
|
+
minKeyframeIntervalMs: Infinity,
|
|
87
|
+
nonKeyframeCount: 0,
|
|
88
|
+
pendingDiscontinuity: pendingDiscontinuity ?? false,
|
|
89
|
+
segmentDurations: new Map(),
|
|
90
|
+
segmentFirstMoofChecked: false,
|
|
91
|
+
segmentIndex: startingSegmentIndex ?? 0,
|
|
92
|
+
segmentStartTime: Date.now(),
|
|
93
|
+
segmentTrackDurations: new Map(),
|
|
94
|
+
segmentsWithoutLeadingKeyframe: 0,
|
|
95
|
+
stopped: false,
|
|
96
|
+
totalKeyframeIntervalMs: 0,
|
|
97
|
+
trackExpectedDurations: new Map(),
|
|
98
|
+
trackTimescales: new Map(),
|
|
99
|
+
trackTimestamps: initialTrackTimestamps ? new Map(initialTrackTimestamps) : new Map(),
|
|
100
|
+
zeroDurationWarned: new Set()
|
|
101
|
+
};
|
|
102
|
+
// Reference to the input stream for cleanup.
|
|
103
|
+
let inputStream = null;
|
|
104
|
+
/**
|
|
105
|
+
* Generates the m3u8 playlist content.
|
|
106
|
+
*/
|
|
107
|
+
function generatePlaylist() {
|
|
108
|
+
// Compute TARGETDURATION from the maximum actual segment duration in the current playlist window. RFC 8216 requires this value to be an integer that is greater
|
|
109
|
+
// than or equal to every #EXTINF duration in the playlist. We floor at the configured segment duration to avoid under-declaring when all segments are short.
|
|
110
|
+
const startIndex = Math.max(0, state.segmentIndex - CONFIG.hls.maxSegments);
|
|
111
|
+
let maxDuration = CONFIG.hls.segmentDuration;
|
|
112
|
+
for (let i = startIndex; i < state.segmentIndex; i++) {
|
|
113
|
+
const duration = state.segmentDurations.get(i) ?? CONFIG.hls.segmentDuration;
|
|
114
|
+
if (duration > maxDuration) {
|
|
115
|
+
maxDuration = duration;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const lines = [
|
|
119
|
+
"#EXTM3U",
|
|
120
|
+
"#EXT-X-VERSION:7",
|
|
121
|
+
["#EXT-X-TARGETDURATION:", String(Math.ceil(maxDuration))].join(""),
|
|
122
|
+
["#EXT-X-MEDIA-SEQUENCE:", String(startIndex)].join(""),
|
|
123
|
+
["#EXT-X-MAP:URI=\"init.mp4?v=", String(state.initVersion), "\""].join("")
|
|
124
|
+
];
|
|
125
|
+
// Add segment entries for each segment in the current playlist window.
|
|
126
|
+
for (let i = startIndex; i < state.segmentIndex; i++) {
|
|
127
|
+
// Add discontinuity marker before segments that follow a recovery event. Re-emit the init segment reference so clients explicitly reinitialize the decoder
|
|
128
|
+
// with the current codec parameters.
|
|
129
|
+
if (state.discontinuityIndices.has(i)) {
|
|
130
|
+
lines.push("#EXT-X-DISCONTINUITY");
|
|
131
|
+
lines.push(["#EXT-X-MAP:URI=\"init.mp4?v=", String(state.initVersion), "\""].join(""));
|
|
132
|
+
}
|
|
133
|
+
// Use the actual recorded duration for this segment. Fall back to the configured target duration for segments that predate duration tracking (e.g. after
|
|
134
|
+
// a hot restart with continuation).
|
|
135
|
+
const duration = state.segmentDurations.get(i) ?? CONFIG.hls.segmentDuration;
|
|
136
|
+
lines.push(["#EXTINF:", duration.toFixed(3), ","].join(""));
|
|
137
|
+
lines.push(["segment", String(i), ".m4s"].join(""));
|
|
138
|
+
}
|
|
139
|
+
lines.push("");
|
|
140
|
+
return lines.join("\n");
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Resets per-segment tracking state. Called after outputting a segment. Extracted to avoid duplication of the same four assignments.
|
|
144
|
+
*/
|
|
145
|
+
function resetSegmentTracking() {
|
|
146
|
+
state.fragmentBuffer = [];
|
|
147
|
+
state.segmentFirstMoofChecked = false;
|
|
148
|
+
state.segmentStartTime = Date.now();
|
|
149
|
+
state.segmentTrackDurations = new Map();
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Outputs the current fragment buffer as a segment.
|
|
153
|
+
*/
|
|
154
|
+
function outputSegment() {
|
|
155
|
+
if (state.fragmentBuffer.length === 0) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
// If this segment follows a tab replacement, record its index for discontinuity marking.
|
|
159
|
+
if (state.pendingDiscontinuity) {
|
|
160
|
+
state.discontinuityIndices.add(state.segmentIndex);
|
|
161
|
+
state.pendingDiscontinuity = false;
|
|
162
|
+
}
|
|
163
|
+
// Compute the segment duration from accumulated trun durations (media timeline). Both audio and video tracks should produce nearly identical real-time values. We
|
|
164
|
+
// take the maximum across tracks to handle edge cases where one track has slightly more data at a segment boundary. Falls back to wall-clock time if no media
|
|
165
|
+
// durations were accumulated (e.g., rewriteMoofTimestamps threw for every moof in this segment, or moov timescale parsing failed). Floored at 0.1 seconds to
|
|
166
|
+
// prevent zero-duration entries that would violate HLS expectations.
|
|
167
|
+
let mediaDuration = 0;
|
|
168
|
+
for (const [trackId, accumulated] of state.segmentTrackDurations) {
|
|
169
|
+
const timescale = state.trackTimescales.get(trackId);
|
|
170
|
+
if (timescale && (accumulated > 0n)) {
|
|
171
|
+
const seconds = Number(accumulated) / timescale;
|
|
172
|
+
if (seconds > mediaDuration) {
|
|
173
|
+
mediaDuration = seconds;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const actualDuration = Math.max(0.1, (mediaDuration > 0) ? mediaDuration : ((Date.now() - state.segmentStartTime) / 1000));
|
|
178
|
+
state.segmentDurations.set(state.segmentIndex, actualDuration);
|
|
179
|
+
// Combine all fragment data into a single segment.
|
|
180
|
+
const segmentData = Buffer.concat(state.fragmentBuffer);
|
|
181
|
+
const segmentName = ["segment", String(state.segmentIndex), ".m4s"].join("");
|
|
182
|
+
// Store the segment and update size for health monitoring.
|
|
183
|
+
storeSegment(streamId, segmentName, segmentData);
|
|
184
|
+
state.lastSegmentSize = segmentData.length;
|
|
185
|
+
// Increment segment index and mark the first segment as emitted.
|
|
186
|
+
state.segmentIndex++;
|
|
187
|
+
state.firstSegmentEmitted = true;
|
|
188
|
+
// Prune duration entries outside the playlist sliding window to prevent unbounded growth.
|
|
189
|
+
const pruneThreshold = Math.max(0, state.segmentIndex - CONFIG.hls.maxSegments);
|
|
190
|
+
for (const idx of state.segmentDurations.keys()) {
|
|
191
|
+
if (idx < pruneThreshold) {
|
|
192
|
+
state.segmentDurations.delete(idx);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
// Clear the fragment buffer and reset segment-level tracking for the next segment.
|
|
196
|
+
resetSegmentTracking();
|
|
197
|
+
// Update the playlist.
|
|
198
|
+
updatePlaylist(streamId, generatePlaylist());
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Processes keyframe detection results for a moof box. Updates running statistics and logs warnings when segments don't start with keyframes.
|
|
202
|
+
*/
|
|
203
|
+
function trackKeyframe(isKeyframe) {
|
|
204
|
+
const now = Date.now();
|
|
205
|
+
if (isKeyframe === true) {
|
|
206
|
+
state.keyframeCount++;
|
|
207
|
+
// Compute the interval from the previous keyframe. We need at least one prior keyframe for a meaningful interval.
|
|
208
|
+
if (state.lastKeyframeTime !== null) {
|
|
209
|
+
const intervalMs = now - state.lastKeyframeTime;
|
|
210
|
+
state.totalKeyframeIntervalMs += intervalMs;
|
|
211
|
+
if (intervalMs < state.minKeyframeIntervalMs) {
|
|
212
|
+
state.minKeyframeIntervalMs = intervalMs;
|
|
213
|
+
}
|
|
214
|
+
if (intervalMs > state.maxKeyframeIntervalMs) {
|
|
215
|
+
state.maxKeyframeIntervalMs = intervalMs;
|
|
216
|
+
}
|
|
217
|
+
LOG.debug("streaming:segmenter", "Keyframe detected, interval: %dms.", intervalMs);
|
|
218
|
+
}
|
|
219
|
+
state.lastKeyframeTime = now;
|
|
220
|
+
}
|
|
221
|
+
else if (isKeyframe === false) {
|
|
222
|
+
state.nonKeyframeCount++;
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
state.indeterminateCount++;
|
|
226
|
+
}
|
|
227
|
+
// Check if this is the first moof in the current segment. A segment that doesn't start with a keyframe may cause frozen frames in downstream consumers.
|
|
228
|
+
if (!state.segmentFirstMoofChecked) {
|
|
229
|
+
state.segmentFirstMoofChecked = true;
|
|
230
|
+
if (isKeyframe !== true) {
|
|
231
|
+
state.segmentsWithoutLeadingKeyframe++;
|
|
232
|
+
LOG.warn("Segment %d does not start with a keyframe.", state.segmentIndex);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Handles a parsed MP4 box.
|
|
238
|
+
*/
|
|
239
|
+
function handleBox(box) {
|
|
240
|
+
if (state.stopped) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
// Handle init segment boxes (ftyp, moov).
|
|
244
|
+
if (!state.hasInit) {
|
|
245
|
+
if ((box.type === "ftyp") || (box.type === "moov")) {
|
|
246
|
+
state.initBoxes.push(box.data);
|
|
247
|
+
// Check if we have both ftyp and moov.
|
|
248
|
+
if (box.type === "moov") {
|
|
249
|
+
// Output the init segment.
|
|
250
|
+
const initData = Buffer.concat(state.initBoxes);
|
|
251
|
+
storeInitSegment(streamId, initData);
|
|
252
|
+
state.hasInit = true;
|
|
253
|
+
state.initSegment = initData;
|
|
254
|
+
// Check whether the init content changed. Always true for fresh streams (no previousInitSegment). For tab replacement, false when the new capture
|
|
255
|
+
// happens to use the same codec parameters as the old one.
|
|
256
|
+
const initChanged = !previousInitSegment || !initData.equals(previousInitSegment);
|
|
257
|
+
// Version the init URI for HLS cache busting. Incrementing the version makes the #EXT-X-MAP URI different from the previous playlist, forcing clients
|
|
258
|
+
// to re-fetch the init segment. This prevents timescale mismatches when Chrome's MediaRecorder picks a different timescale between capture sessions.
|
|
259
|
+
if (initChanged) {
|
|
260
|
+
state.initVersion++;
|
|
261
|
+
}
|
|
262
|
+
// Extract per-track timescale values from the moov box. These convert trun sample durations (timescale units) to seconds for media-time EXTINF values.
|
|
263
|
+
// Wrapped in try/catch so a malformed moov never prevents stream startup — EXTINF falls back to wall-clock time if parsing fails.
|
|
264
|
+
try {
|
|
265
|
+
state.trackTimescales = parseMoovTimescales(box.data);
|
|
266
|
+
if (state.trackTimescales.size === 0) {
|
|
267
|
+
LOG.debug("streaming:segmenter", "No track timescales found in moov. EXTINF will use wall-clock fallback.");
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
LOG.debug("streaming:segmenter", "Failed to parse moov timescales. EXTINF will use wall-clock fallback.");
|
|
272
|
+
}
|
|
273
|
+
// Log init segment details for debugging timescale or codec issues.
|
|
274
|
+
const timescaleEntries = [];
|
|
275
|
+
for (const [trackId, timescale] of state.trackTimescales) {
|
|
276
|
+
timescaleEntries.push("track " + String(trackId) + "=" + String(timescale));
|
|
277
|
+
}
|
|
278
|
+
LOG.debug("streaming:segmenter", "Init segment received: %d bytes, version=%d, timescales=[%s].", initData.length, state.initVersion, timescaleEntries.join(", "));
|
|
279
|
+
// Suppress the discontinuity marker when codec parameters are unchanged (byte-identical init). This avoids an unnecessary decoder flush on the client.
|
|
280
|
+
if (!initChanged && state.pendingDiscontinuity) {
|
|
281
|
+
state.pendingDiscontinuity = false;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
// Handle media fragment boxes (moof, mdat).
|
|
288
|
+
if (box.type === "moof") {
|
|
289
|
+
// Start of a new fragment. Check whether we should cut a segment before adding this moof to the buffer.
|
|
290
|
+
if (state.fragmentBuffer.length > 0) {
|
|
291
|
+
if (!state.firstSegmentEmitted) {
|
|
292
|
+
// Fast path: emit the first segment as soon as we have one complete moof+mdat pair.
|
|
293
|
+
outputSegment();
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
const elapsedMs = Date.now() - state.segmentStartTime;
|
|
297
|
+
const targetMs = CONFIG.hls.segmentDuration * 1000;
|
|
298
|
+
if (elapsedMs >= targetMs) {
|
|
299
|
+
outputSegment();
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
// Rewrite tfdt.baseMediaDecodeTime in each traf with self-generated monotonic timestamps. This eliminates discontinuities from capture restarts (tab
|
|
304
|
+
// replacement, source reload). Wrapped in try/catch so a malformed moof never crashes the segmenter — the segment passes through with Chrome's original
|
|
305
|
+
// timestamps, which is better than dropping it entirely.
|
|
306
|
+
try {
|
|
307
|
+
const trafDurations = rewriteMoofTimestamps(box.data, state.trackTimestamps);
|
|
308
|
+
// Process per-track durations for diagnostics and sanity checking.
|
|
309
|
+
for (const [trackId, duration] of trafDurations) {
|
|
310
|
+
// Zero duration: rate-limited warning. Indicates a trun parsing issue but doesn't corrupt the counter (advancing by zero is harmless).
|
|
311
|
+
if (duration === 0n) {
|
|
312
|
+
if (!state.zeroDurationWarned.has(trackId)) {
|
|
313
|
+
state.zeroDurationWarned.add(trackId);
|
|
314
|
+
LOG.debug("streaming:segmenter", "Zero duration in moof traf for track %d.", trackId);
|
|
315
|
+
}
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
// The effective duration is what trackTimestamps actually advances by for this moof. Normally this equals the trun-computed duration, but the sanity
|
|
319
|
+
// check below may clamp it to the expected baseline when the computed value is unreasonable.
|
|
320
|
+
let effectiveDuration = duration;
|
|
321
|
+
// Sanity check: compare against the anchored baseline for this track. A wildly off duration (e.g., from a corrupt trun with inflated per-sample durations)
|
|
322
|
+
// would permanently shift all subsequent timestamps if not caught. The rewrite function already advanced the counter by `duration` — we correct it here
|
|
323
|
+
// by substituting the baseline value when the computed value is unreasonable. The baseline is anchored to the first valid moof and never updated — this
|
|
324
|
+
// prevents baseline poisoning from burst moofs that Chrome's MediaRecorder produces after source buffering stalls.
|
|
325
|
+
const expected = state.trackExpectedDurations.get(trackId);
|
|
326
|
+
if (expected && (expected > 0n) && ((duration > (expected * DURATION_SANITY_RATIO)) || (duration < (expected / DURATION_SANITY_RATIO)))) {
|
|
327
|
+
const current = state.trackTimestamps.get(trackId);
|
|
328
|
+
if (current !== undefined) {
|
|
329
|
+
state.trackTimestamps.set(trackId, current - duration + expected);
|
|
330
|
+
}
|
|
331
|
+
effectiveDuration = expected;
|
|
332
|
+
LOG.debug("streaming:segmenter", "Clamped abnormal trun duration for track %d: %s units (expected ~%s, baselines=%d).", trackId, String(duration), String(expected), state.trackExpectedDurations.size);
|
|
333
|
+
}
|
|
334
|
+
else if (!state.trackExpectedDurations.has(trackId)) {
|
|
335
|
+
// Establish baseline from the first valid moof per track. The baseline is never updated afterward — see trackExpectedDurations comment in SegmenterState.
|
|
336
|
+
state.trackExpectedDurations.set(trackId, duration);
|
|
337
|
+
LOG.debug("streaming:segmenter", "Anchored baseline for track %d: %s units (segment %d).", trackId, String(duration), state.segmentIndex);
|
|
338
|
+
}
|
|
339
|
+
// Accumulate the effective duration for media-time EXTINF computation. This stays synchronized with the trackTimestamps counter that drives
|
|
340
|
+
// baseMediaDecodeTime — clamped moofs contribute the clamped value, normal moofs contribute the trun-computed value.
|
|
341
|
+
const prev = state.segmentTrackDurations.get(trackId) ?? 0n;
|
|
342
|
+
state.segmentTrackDurations.set(trackId, prev + effectiveDuration);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
catch {
|
|
346
|
+
LOG.debug("streaming:segmenter", "Failed to rewrite moof timestamps.");
|
|
347
|
+
}
|
|
348
|
+
// When keyframe debugging is enabled, parse traf/trun sample flags to detect whether this moof starts with a keyframe. Wrapped in try/catch for failure
|
|
349
|
+
// isolation — a malformed moof should never crash the segmenter.
|
|
350
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
351
|
+
if (KEYFRAME_DEBUG) {
|
|
352
|
+
try {
|
|
353
|
+
const isKeyframe = detectMoofKeyframe(box.data);
|
|
354
|
+
trackKeyframe(isKeyframe);
|
|
355
|
+
}
|
|
356
|
+
catch {
|
|
357
|
+
state.indeterminateCount++;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
// Add moof to the fragment buffer.
|
|
361
|
+
state.fragmentBuffer.push(box.data);
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
if (box.type === "mdat") {
|
|
365
|
+
// Add mdat to the fragment buffer.
|
|
366
|
+
state.fragmentBuffer.push(box.data);
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
// Other box types (styp, sidx, etc.) are passed through to the current segment.
|
|
370
|
+
if (state.hasInit) {
|
|
371
|
+
state.fragmentBuffer.push(box.data);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
// Create the MP4 box parser.
|
|
375
|
+
const parser = createMP4BoxParser(handleBox);
|
|
376
|
+
/**
|
|
377
|
+
* Handles data from the input stream.
|
|
378
|
+
*/
|
|
379
|
+
function handleData(chunk) {
|
|
380
|
+
if (state.stopped) {
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
try {
|
|
384
|
+
parser.push(chunk);
|
|
385
|
+
}
|
|
386
|
+
catch (error) {
|
|
387
|
+
onError(error instanceof Error ? error : new Error(String(error)));
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Handles the end of the input stream.
|
|
392
|
+
*/
|
|
393
|
+
function handleEnd() {
|
|
394
|
+
if (state.stopped) {
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
// Output any remaining data as a final segment.
|
|
398
|
+
if (state.fragmentBuffer.length > 0) {
|
|
399
|
+
outputSegment();
|
|
400
|
+
}
|
|
401
|
+
state.stopped = true;
|
|
402
|
+
parser.flush();
|
|
403
|
+
onStop();
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Handles input stream errors.
|
|
407
|
+
*/
|
|
408
|
+
function handleError(error) {
|
|
409
|
+
if (state.stopped) {
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
state.stopped = true;
|
|
413
|
+
parser.flush();
|
|
414
|
+
onError(error);
|
|
415
|
+
}
|
|
416
|
+
return {
|
|
417
|
+
getInitSegment: () => state.initSegment,
|
|
418
|
+
getInitVersion: () => state.initVersion,
|
|
419
|
+
getKeyframeStats: () => ({
|
|
420
|
+
averageKeyframeIntervalMs: (state.keyframeCount >= 2) ? (state.totalKeyframeIntervalMs / (state.keyframeCount - 1)) : 0,
|
|
421
|
+
indeterminateCount: state.indeterminateCount,
|
|
422
|
+
keyframeCount: state.keyframeCount,
|
|
423
|
+
maxKeyframeIntervalMs: (state.keyframeCount >= 2) ? state.maxKeyframeIntervalMs : 0,
|
|
424
|
+
minKeyframeIntervalMs: (state.keyframeCount >= 2) ? state.minKeyframeIntervalMs : 0,
|
|
425
|
+
nonKeyframeCount: state.nonKeyframeCount,
|
|
426
|
+
segmentsWithoutLeadingKeyframe: state.segmentsWithoutLeadingKeyframe
|
|
427
|
+
}),
|
|
428
|
+
getLastSegmentSize: () => state.lastSegmentSize,
|
|
429
|
+
getSegmentIndex: () => state.segmentIndex,
|
|
430
|
+
getTrackTimestamps: () => new Map(state.trackTimestamps),
|
|
431
|
+
markDiscontinuity: () => {
|
|
432
|
+
if (state.stopped) {
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
// Flush any accumulated fragments as a short segment so pre-recovery and post-recovery content are cleanly separated.
|
|
436
|
+
outputSegment();
|
|
437
|
+
state.pendingDiscontinuity = true;
|
|
438
|
+
},
|
|
439
|
+
pipe: (stream) => {
|
|
440
|
+
inputStream = stream;
|
|
441
|
+
stream.on("data", handleData);
|
|
442
|
+
stream.on("end", handleEnd);
|
|
443
|
+
stream.on("error", handleError);
|
|
444
|
+
},
|
|
445
|
+
stop: () => {
|
|
446
|
+
if (state.stopped) {
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
state.stopped = true;
|
|
450
|
+
// Remove listeners from input stream.
|
|
451
|
+
if (inputStream) {
|
|
452
|
+
inputStream.removeListener("data", handleData);
|
|
453
|
+
inputStream.removeListener("end", handleEnd);
|
|
454
|
+
inputStream.removeListener("error", handleError);
|
|
455
|
+
}
|
|
456
|
+
// Flush the parser.
|
|
457
|
+
parser.flush();
|
|
458
|
+
}
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
//# sourceMappingURL=fmp4Segmenter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fmp4Segmenter.js","sourceRoot":"","sources":["../../src/streaming/fmp4Segmenter.ts"],"names":[],"mappings":"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"}
|