@torrent-tv/proxy 2.90.0 → 2.92.0

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.
Files changed (146) hide show
  1. package/bin/cli.js +5 -2
  2. package/package.json +18 -2
  3. package/routes/api/playback-plan/post.js +21 -4
  4. package/routes/api/sources/files/get.js +20 -28
  5. package/routes/api/sources/post.js +4 -2
  6. package/routes/api/sources/stats/get.js +4 -11
  7. package/routes/api/sources/viewer/post.js +16 -0
  8. package/routes/api/sources/warm/post.js +6 -215
  9. package/routes/api/subtitles/get.js +22 -125
  10. package/routes/api/subtitles/post.js +26 -0
  11. package/routes/api/transcode-sessions/post.js +24 -2
  12. package/routes/media/get.js +80 -0
  13. package/routes/stream/get.js +26 -94
  14. package/routes/transcode/audio-warm/get.js +9 -21
  15. package/routes/transcode/variant-warm/get.js +8 -14
  16. package/server.js +410 -46
  17. package/services/encode/AdmittedInput.js +136 -0
  18. package/services/encode/CushionReport.js +1 -76
  19. package/services/encode/EncodeInputs.js +142 -0
  20. package/services/encode/EncodeOrchestrator.js +4 -1
  21. package/services/encode/EncodeRun.js +13 -4
  22. package/services/encode/EncodeRuns.js +132 -52
  23. package/services/encode/InputFailures.js +37 -0
  24. package/services/encode/MatroskaInput.js +158 -0
  25. package/services/encode/OutputOpening.js +16 -64
  26. package/services/encode/Renditions.js +1 -17
  27. package/services/encode/admitted-command.js +60 -0
  28. package/services/encode/annex-b-input.js +37 -0
  29. package/services/encode/bit-payload.js +29 -0
  30. package/services/encode/output/OutputCatalog.js +1 -1
  31. package/services/encode/output/Timeline.js +11 -0
  32. package/services/encode/pcm-normalization.js +30 -0
  33. package/services/encode/piece-completeness.js +54 -1
  34. package/services/encode/quality/HostLoad.js +19 -73
  35. package/services/encode/quality/QualityOffer.js +1 -1
  36. package/services/encode/run-command.js +1 -589
  37. package/services/encode/segment-formats/mp4-boxes.js +3 -1
  38. package/services/encode/segment-formats/presentation-segment.js +114 -0
  39. package/services/media/ContainerOrchestrator.js +118 -48
  40. package/services/media/KeyframeTables.js +16 -115
  41. package/services/media/MediaReadRequests.js +120 -0
  42. package/services/media/ProbeRequests.js +135 -0
  43. package/services/media/SegmentInputs.js +48 -0
  44. package/services/media/SourceFile.js +6 -0
  45. package/services/media/SubtitleOrchestrator.js +190 -4
  46. package/services/media/await-plan.js +47 -0
  47. package/services/media/container/AsfContainer.js +217 -0
  48. package/services/media/container/AviContainer.js +343 -34
  49. package/services/media/container/CadencedPictureOrder.js +45 -0
  50. package/services/media/container/Container.js +7 -3
  51. package/services/media/container/ContainerFactory.js +10 -3
  52. package/services/media/container/FfprobeContainer.js +222 -0
  53. package/services/media/container/MatroskaContainer.js +72 -46
  54. package/services/media/container/Mp4Container.js +472 -139
  55. package/services/media/container/MpegPsContainer.js +281 -0
  56. package/services/media/container/MpegTsContainer.js +277 -0
  57. package/services/media/container/PacketIndex.js +270 -0
  58. package/services/media/container/PacketRecords.js +237 -0
  59. package/services/media/container/RetainedBytes.js +39 -0
  60. package/services/media/container/RetainedReads.js +51 -0
  61. package/services/media/container/ac3-frame.js +46 -0
  62. package/services/media/container/asf-packets.js +153 -0
  63. package/services/media/container/avc-picture-order.js +50 -0
  64. package/services/media/container/avi-open-dml.js +47 -0
  65. package/services/media/container/dts-frame.js +51 -0
  66. package/services/media/container/ebml-stream.js +12 -1
  67. package/services/media/container/ffprobe-record.js +45 -0
  68. package/services/media/container/flv-packet-ranges.js +27 -0
  69. package/services/media/container/following-packet-time.js +37 -0
  70. package/services/media/container/h264-configuration.js +183 -0
  71. package/services/media/container/h264-elementary-index.js +245 -0
  72. package/services/media/container/hevc-configuration.js +165 -0
  73. package/services/media/container/hevc-elementary-index.js +132 -0
  74. package/services/media/container/hevc-picture-order.js +66 -0
  75. package/services/media/container/latm-frame.js +99 -0
  76. package/services/media/container/matroska-packets.js +207 -0
  77. package/services/media/container/memory-unavailable.js +8 -0
  78. package/services/media/container/mp3-packet-dependencies.js +41 -0
  79. package/services/media/container/mp4-fragments.js +148 -0
  80. package/services/media/container/mpeg-audio-frame.js +19 -0
  81. package/services/media/container/mpeg-audio-reservoir.js +27 -0
  82. package/services/media/container/mpeg-elementary-index.js +196 -0
  83. package/services/media/container/mpeg-sequence-reader.js +69 -0
  84. package/services/media/container/mpeg-ts-packets.js +93 -0
  85. package/services/media/container/mpeg-video-configuration.js +50 -0
  86. package/services/media/container/mpeg4-picture-timing.js +108 -0
  87. package/services/media/container/pes-header.js +34 -0
  88. package/services/media/playback-declarations.js +57 -0
  89. package/services/media/playback-planner.js +72 -230
  90. package/services/media/probe-packets.js +61 -0
  91. package/services/media/read-playback-declarations.js +18 -0
  92. package/services/media/tracks/AudioTrack.js +9 -0
  93. package/services/media/tracks/TextSubtitleTrack.js +4 -1
  94. package/services/server/OutputLifecycle.js +2 -0
  95. package/services/server/SegmentServing.js +40 -33
  96. package/services/server/ViewerRequests.js +22 -41
  97. package/services/server/controllers/PlaybackController.js +4 -0
  98. package/services/server/controllers/SubtitleController.js +8 -29
  99. package/services/server/transcode-session-files.js +58 -99
  100. package/services/server/wire-outputs.js +58 -15
  101. package/services/storage/IndexMemory.js +85 -0
  102. package/services/storage/MachineBudget.js +27 -3
  103. package/services/storage/allowance.js +8 -1
  104. package/services/storage/piece-store/piece-disk-store.js +18 -3
  105. package/services/storage/piece-store/piece-lru.js +8 -4
  106. package/services/storage/piece-store/shared-piece-store.js +26 -11
  107. package/services/storage/segment-store/SegmentStore.js +49 -18
  108. package/services/storage/wire.js +7 -0
  109. package/services/torrent/Contents.js +52 -20
  110. package/services/torrent/demand/DemandRegister.js +13 -4
  111. package/services/torrent/demand/Window.js +13 -2
  112. package/services/torrent/demand-progress.js +25 -0
  113. package/services/torrent/download/DeadlineScheduler.js +167 -0
  114. package/services/torrent/download/FutureDownload.js +166 -0
  115. package/services/torrent/download/SwarmSelection.js +30 -59
  116. package/services/torrent/download/registry.js +45 -5
  117. package/services/torrent/download/withdraw-claim.js +2 -0
  118. package/services/torrent/torrent-pool.js +39 -600
  119. package/services/torrent/worker/channel.js +8 -1
  120. package/services/torrent/worker/client.js +74 -72
  121. package/services/torrent/worker/files-in-use.js +9 -0
  122. package/services/torrent/worker/handle-state.js +12 -0
  123. package/services/torrent/worker/held-bytes.js +82 -99
  124. package/services/torrent/worker/held-file-bytes.js +17 -0
  125. package/services/torrent/worker/piece-reader.js +67 -683
  126. package/services/torrent/worker/pool-adapter.js +37 -123
  127. package/services/torrent/worker/protocol.js +6 -17
  128. package/services/torrent/worker/whole-file-stats.js +2 -0
  129. package/services/torrent/worker/worker.js +86 -156
  130. package/services/transport/data-channel-handler.js +56 -13
  131. package/services/transport/tunnel-client.js +111 -20
  132. package/services/viewer/DownloadMaps.js +168 -0
  133. package/services/viewer/NativeSourceMap.js +33 -0
  134. package/services/viewer/PriorityMap.js +49 -40
  135. package/services/viewer/PriorityOrchestrator.js +42 -13
  136. package/services/viewer/SourcePreparation.js +278 -0
  137. package/services/viewer/Viewer.js +11 -0
  138. package/services/viewer/Viewers.js +109 -2
  139. package/services/viewer/playback-readiness.js +50 -63
  140. package/services/viewer/segment-demands.js +37 -0
  141. package/services/viewer/urgent-output-ready.js +26 -0
  142. package/services/encode/run-inputs.js +0 -53
  143. package/services/media/keyframe-probe.js +0 -157
  144. package/services/media/media-info-probe.js +0 -93
  145. package/services/torrent/worker/background-fill.js +0 -213
  146. package/services/torrent/worker/resume-warm.js +0 -107
@@ -0,0 +1,245 @@
1
+ import { avcConfigurationFromUnits, avcOrderParameters } from "./h264-configuration.js";
2
+ import { AvcPictureOrder } from "./avc-picture-order.js";
3
+ import { CadencedPictureOrder } from "./CadencedPictureOrder.js";
4
+
5
+ /** Incremental AVC access units, retaining addresses rather than frame payloads. */
6
+ export class H264ElementaryIndex {
7
+ #track;
8
+ #index;
9
+ #position = 0;
10
+ #zeroes = 0;
11
+ #nal = null;
12
+ #mappings = [];
13
+ #stamps = [];
14
+ #sets = new Map();
15
+ #frame = null;
16
+ #prefix = 0;
17
+ #recent = [];
18
+ #picStruct = null;
19
+ #order = null;
20
+ #orderUsesClockTicks = false;
21
+ #anchor = null;
22
+ #cadenced = null;
23
+
24
+ constructor(track, index) {
25
+ this.#track = track; this.#index = index;
26
+ if (track.presentationCadenceSeconds > 0) this.#cadenced = new CadencedPictureOrder(track, index);
27
+ }
28
+
29
+ push(bytes, sourceStart, stamp) {
30
+ const from = this.#position;
31
+ this.#mappings.push({ from, to: from + bytes.length, sourceStart });
32
+ if (stamp?.pts !== null && stamp?.pts !== undefined) this.#stamps.push({ position: from, pts: stamp.pts, dts: stamp.dts ?? stamp.pts });
33
+ for (const byte of bytes) {
34
+ if (byte === 1 && this.#zeroes >= 2) {
35
+ const start = this.#position - this.#zeroes;
36
+ if (this.#nal) this.#finishNal(start);
37
+ this.#nal = { start, payloadStart: this.#position + 1, bytes: [], type: null, sei: null, seiZeroes: 0 };
38
+ } else if (this.#nal) {
39
+ if (this.#nal.type === null) {
40
+ if (byte & 128 || !(byte & 31)) throw new Error("AVC NAL header is invalid.");
41
+ this.#nal.type = byte & 31;
42
+ if (this.#nal.type === 6) this.#nal.sei = new SeiTiming();
43
+ } else if (this.#nal.sei) {
44
+ if (byte === 0) this.#nal.seiZeroes++;
45
+ else {
46
+ while (this.#nal.seiZeroes > 0) {
47
+ this.#nal.sei.push(0);
48
+ this.#nal.seiZeroes--;
49
+ }
50
+ this.#nal.sei.push(byte);
51
+ }
52
+ }
53
+ // Retain only the slice prefix needed for macroblock and picture order.
54
+ const limit = [7, 8].includes(this.#nal.type) ? 65535 : 64;
55
+ if (this.#nal.bytes.length < limit) this.#nal.bytes.push(byte);
56
+ else if ([7, 8].includes(this.#nal.type)) throw new Error("AVC parameter set exceeds its decoder declaration.");
57
+ } else if (byte !== 0) throw new Error("AVC bytes precede their first start code.");
58
+ this.#zeroes = byte === 0 ? this.#zeroes + 1 : 0;
59
+ this.#position++;
60
+ }
61
+ }
62
+
63
+ complete() {
64
+ if (this.#nal) this.#finishNal(this.#position - this.#zeroes);
65
+ if (this.#track.picStructPresent && this.#frame?.picStruct === null) throw new Error("AVC final access-unit duration requires its SEI picture structure.");
66
+ const ticks = this.#frame?.picStruct === null ? this.#track.frameOnly ? 2 : null
67
+ : [2, 1, 1, 2, 2, 3, 3, 4, 6][this.#frame?.picStruct];
68
+ const finalDuration = ticks && this.#track.timingTickSeconds > 0
69
+ ? ticks * this.#track.timingTickSeconds : this.#track.fps > 0 ? 1 / this.#track.fps : null;
70
+ if (this.#frame) this.#close(this.#position - this.#zeroes, finalDuration);
71
+ this.#cadenced?.finish();
72
+ this.#index.complete(this.#track.trackNumber);
73
+ }
74
+
75
+ #finishNal(end) {
76
+ const nal = this.#nal;
77
+ if (end <= nal.payloadStart || nal.type === null) throw new Error("AVC NAL unit is empty.");
78
+ if ([2, 3, 4, 19, 20, 21].includes(nal.type)) throw new Error("AVC partitioned or extended pictures require their own access-unit indexing.");
79
+ const size = end - nal.payloadStart;
80
+ const bytes = Buffer.from(nal.bytes.slice(0, size));
81
+ if (nal.sei) {
82
+ const timing = nal.sei.finish();
83
+ if (timing && this.#track.picStructPresent) this.#picStruct = readPicStruct(timing, this.#track.seiDelayBits ?? 0);
84
+ }
85
+ if ([7, 8].includes(nal.type)) {
86
+ const previous = this.#sets.get(nal.type);
87
+ if (previous && !previous.equals(bytes)) throw new Error("AVC parameter-set changes require packet-specific declarations.");
88
+ this.#sets.set(nal.type, bytes);
89
+ if (this.#sets.has(7) && this.#sets.has(8)) Object.assign(this.#track,
90
+ avcConfigurationFromUnits(this.#sets.get(7), this.#sets.get(8)));
91
+ if (this.#sets.has(7) && this.#sets.has(8) && !this.#order) {
92
+ const parameters = avcOrderParameters(this.#sets.get(7), this.#sets.get(8));
93
+ this.#order = new AvcPictureOrder(parameters);
94
+ // Types 1/2 declare order, not proportional elapsed clock ticks.
95
+ this.#orderUsesClockTicks = parameters.type === 0;
96
+ }
97
+ }
98
+ if ([6, 7, 8, 9].includes(nal.type) && this.#frame) {
99
+ if (this.#prefix === null) this.#prefix = nal.start;
100
+ return;
101
+ }
102
+ if (![1, 5].includes(nal.type)) return;
103
+ const firstMacroblock = firstUe(bytes.subarray(1));
104
+ if (this.#frame && firstMacroblock !== 0) {
105
+ this.#frame.keyframe ||= nal.type === 5;
106
+ return;
107
+ }
108
+ if (!this.#track.codecPrivateB64) throw new Error("AVC picture precedes its decoder configuration.");
109
+ const start = this.#prefix ?? nal.start;
110
+ let stamp = null;
111
+ while (this.#stamps[0]?.position <= nal.start) stamp = this.#stamps.shift();
112
+ let pictureOrder = null;
113
+ if (this.#track.frameOnly && !this.#track.picStructPresent) {
114
+ try { pictureOrder = this.#order?.read(bytes); }
115
+ catch (error) { if (!stamp) throw error; }
116
+ }
117
+ if (pictureOrder?.idr) this.#anchor = null;
118
+ if (stamp && pictureOrder) this.#anchor = { count: pictureOrder.count, pts: stamp.pts };
119
+ if (this.#cadenced) {
120
+ if (!pictureOrder) throw new Error("Cadenced AVC requires complete progressive picture order.");
121
+ stamp = { pts: 0, dts: this.#frame ? this.#frame.dts + this.#track.presentationCadenceSeconds : this.#track.startTimeSeconds };
122
+ } else if (!stamp) {
123
+ if (!pictureOrder || !this.#orderUsesClockTicks || !this.#anchor || !this.#frame || !(this.#track.timingTickSeconds > 0)) {
124
+ throw new Error("AVC access unit has neither a PES timestamp nor complete timing declarations.");
125
+ }
126
+ stamp = { pts: this.#anchor.pts + (pictureOrder.count - this.#anchor.count) * this.#track.timingTickSeconds,
127
+ dts: this.#frame.dts + 2 * this.#track.timingTickSeconds };
128
+ }
129
+ if (this.#frame) {
130
+ const duration = stamp.dts - this.#frame.dts;
131
+ if (!(duration > 0)) throw new Error("AVC decode timestamps do not advance.");
132
+ this.#close(start, duration);
133
+ }
134
+ this.#frame = { start, pts: stamp.pts, dts: stamp.dts, keyframe: nal.type === 5, picStruct: this.#picStruct,
135
+ pictureOrder: pictureOrder?.count, orderReset: pictureOrder?.idr };
136
+ this.#picStruct = null;
137
+ this.#prefix = null;
138
+ }
139
+
140
+ #close(end, duration) {
141
+ if (!(duration > 0)) throw new Error("AVC final access-unit duration is unavailable.");
142
+ const { start, picStruct: _picStruct, pictureOrder, orderReset, ...timing } = this.#frame;
143
+ const ranges = this.#mappings.filter(range => range.from < end && range.to > start).map(range => [
144
+ range.sourceStart + Math.max(start, range.from) - range.from,
145
+ range.sourceStart + Math.min(end, range.to) - range.from - 1]);
146
+ if (ranges.reduce((sum, [a, b]) => sum + b - a + 1, 0) !== end - start) throw new Error("AVC access-unit addresses are incomplete.");
147
+ const packet = { ...timing, duration, ranges };
148
+ if (this.#cadenced) this.#cadenced.push(packet, pictureOrder, orderReset);
149
+ else this.#index.append(this.#track.trackNumber, packet);
150
+ if (!this.#cadenced) this.#recent.push(timing.pts);
151
+ const depth = this.#track.reorderDepth;
152
+ if (!this.#cadenced && Number.isSafeInteger(depth) && this.#recent.length > depth + 1) {
153
+ const earliest = Math.min(...this.#recent);
154
+ this.#recent.splice(this.#recent.indexOf(earliest), 1);
155
+ this.#index.coverThrough(this.#track.trackNumber, Math.max(0, earliest + duration));
156
+ }
157
+ this.#mappings = this.#mappings.filter(range => range.to > end);
158
+ }
159
+ }
160
+
161
+ /** Skip unrelated SEI payloads without retaining them; timing needs at most 9 bytes. */
162
+ class SeiTiming {
163
+ #phase = "type";
164
+ #type = 0;
165
+ #size = 0;
166
+ #remaining = 0;
167
+ #bytes = [];
168
+ #timing = null;
169
+ #zeroes = 0;
170
+ #escaped = false;
171
+
172
+ push(byte) {
173
+ if (this.#zeroes >= 2 && byte === 3) {
174
+ this.#zeroes = 0;
175
+ this.#escaped = true;
176
+ return;
177
+ }
178
+ if (this.#escaped && byte > 3) throw new Error("AVC SEI escape byte is invalid.");
179
+ this.#escaped = false;
180
+ this.#zeroes = byte === 0 ? this.#zeroes + 1 : 0;
181
+ if (this.#phase === "type") {
182
+ this.#type += byte;
183
+ if (byte !== 255) this.#phase = "size";
184
+ } else if (this.#phase === "size") {
185
+ this.#size += byte;
186
+ if (byte !== 255) {
187
+ this.#remaining = this.#size;
188
+ this.#phase = "payload";
189
+ if (this.#remaining === 0) this.#next();
190
+ }
191
+ } else {
192
+ if (this.#type === 1 && this.#bytes.length < 9) this.#bytes.push(byte);
193
+ if (--this.#remaining === 0) this.#next();
194
+ }
195
+ }
196
+
197
+ #next() {
198
+ if (this.#type === 1) this.#timing = Buffer.from(this.#bytes);
199
+ this.#bytes = [];
200
+ this.#type = 0;
201
+ this.#size = 0;
202
+ this.#phase = "type";
203
+ }
204
+
205
+ finish() {
206
+ if (this.#escaped || !(this.#phase === "size" && this.#type === 128 && this.#size === 0)) {
207
+ throw new Error("AVC SEI payload or trailing bits are incomplete.");
208
+ }
209
+ return this.#timing;
210
+ }
211
+ }
212
+
213
+ function readPicStruct(bytes, offset) {
214
+ if (offset + 4 > bytes.length * 8) throw new Error("AVC SEI picture timing is truncated.");
215
+ let value = 0;
216
+ for (let at = offset; at < offset + 4; at++) value = value * 2 + ((bytes[at >> 3] >> (7 - (at & 7))) & 1);
217
+ if (value > 8) throw new Error("AVC SEI picture structure is reserved.");
218
+ return value;
219
+ }
220
+
221
+ function firstUe(bytes) {
222
+ const raw = [];
223
+ for (let at = 0, zeroes = 0; at < bytes.length; at++) {
224
+ const byte = bytes[at];
225
+ if (zeroes >= 2 && byte === 3) {
226
+ if (at + 1 >= bytes.length || bytes[at + 1] > 3) throw new Error("AVC slice escape byte is invalid.");
227
+ zeroes = 0;
228
+ continue;
229
+ }
230
+ raw.push(byte);
231
+ zeroes = byte === 0 ? zeroes + 1 : 0;
232
+ }
233
+ bytes = Buffer.from(raw);
234
+ let position = 0, zeroes = 0;
235
+ const bit = () => {
236
+ if (position >= bytes.length * 8) throw new Error("AVC slice header is truncated.");
237
+ const value = (bytes[position >> 3] >> (7 - (position & 7))) & 1;
238
+ position++;
239
+ return value;
240
+ };
241
+ while (!bit()) if (++zeroes > 31) throw new Error("AVC first macroblock is invalid.");
242
+ let value = 0;
243
+ for (let at = 0; at < zeroes; at++) value = value * 2 + bit();
244
+ return 2 ** zeroes - 1 + value;
245
+ }
@@ -0,0 +1,165 @@
1
+ import { Bits } from "./h264-configuration.js";
2
+
3
+ /** HEVC SPS ordering limits are declared for every temporal sublayer (H.265 7.3.2.2). */
4
+ export function hevcConfiguration(hvcc) {
5
+ if (!Buffer.isBuffer(hvcc) || hvcc.length < 23 || hvcc[0] !== 1) throw new Error("HEVC decoder configuration is invalid.");
6
+ const sequences = [];
7
+ let at = 23;
8
+ for (let array = 0; array < hvcc[22]; array++) {
9
+ if (at + 3 > hvcc.length) throw new Error("HEVC parameter-set array is truncated.");
10
+ const type = hvcc[at++] & 63;
11
+ const count = hvcc.readUInt16BE(at); at += 2;
12
+ for (let index = 0; index < count; index++) {
13
+ if (at + 2 > hvcc.length) throw new Error("HEVC parameter-set length is truncated.");
14
+ const size = hvcc.readUInt16BE(at); at += 2;
15
+ if (size < 2 || at + size > hvcc.length) throw new Error("HEVC parameter set exceeds its configuration.");
16
+ const unit = hvcc.subarray(at, at + size); at += size;
17
+ if (((unit[0] >> 1) & 63) !== type) throw new Error("HEVC parameter-set array type does not match its NAL unit.");
18
+ if (type === 33) sequences.push(hevcSequence(unit));
19
+ }
20
+ }
21
+ if (!sequences.length || at !== hvcc.length) throw new Error("HEVC configuration has no SPS or has trailing bytes.");
22
+ const first = sequences[0];
23
+ if (sequences.some(sequence => sequence.width !== first.width || sequence.height !== first.height || sequence.bitDepth !== first.bitDepth)) {
24
+ throw new Error("HEVC SPS changes require packet-specific decoder configuration.");
25
+ }
26
+ return { ...first, reorderDepth: Math.max(...sequences.map(sequence => sequence.reorderDepth)), nalLengthBytes: (hvcc[21] & 3) + 1 };
27
+ }
28
+
29
+ export function hevcSequence(nal) {
30
+ if ((nal[0] & 128) || ((nal[0] >> 1) & 63) !== 33 || !(nal[1] & 7)) throw new Error("HEVC SPS NAL header is invalid.");
31
+ if ((nal[0] & 1) || (nal[1] >> 3)) throw new Error("HEVC multilayer SPS requires layer-specific configuration.");
32
+ const bytes = [];
33
+ for (let at = 2, zeroes = 0; at < nal.length; at++) {
34
+ const byte = nal[at];
35
+ if (zeroes >= 2 && byte === 3) {
36
+ if (at + 1 >= nal.length || nal[at + 1] > 3) throw new Error("HEVC escape byte is invalid.");
37
+ zeroes = 0; continue;
38
+ }
39
+ bytes.push(byte); zeroes = byte === 0 ? zeroes + 1 : 0;
40
+ }
41
+ const bits = new Bits(Buffer.from(bytes), "HEVC");
42
+ bits.uint(4);
43
+ const layers = bits.uint(3);
44
+ if (layers > 6) throw new Error("HEVC temporal sublayer count is invalid.");
45
+ bits.uint(1);
46
+ skip(bits, 96);
47
+ const profiles = [], levels = [];
48
+ for (let layer = 0; layer < layers; layer++) { profiles.push(bits.uint(1)); levels.push(bits.uint(1)); }
49
+ if (layers) skip(bits, (8 - layers) * 2);
50
+ for (let layer = 0; layer < layers; layer++) {
51
+ if (profiles[layer]) skip(bits, 88);
52
+ if (levels[layer]) skip(bits, 8);
53
+ }
54
+ const sequenceId = bits.ue(), chroma = bits.ue();
55
+ if (sequenceId > 15 || chroma > 3) throw new Error("HEVC SPS identity or chroma format is invalid.");
56
+ const separate = chroma === 3 ? bits.uint(1) : 0;
57
+ const codedWidth = bits.ue(), codedHeight = bits.ue();
58
+ let left = 0, right = 0, top = 0, bottom = 0;
59
+ if (bits.uint(1)) { left = bits.ue(); right = bits.ue(); top = bits.ue(); bottom = bits.ue(); }
60
+ const luma = bits.ue(), chromaDepth = bits.ue();
61
+ if (luma > 8 || chromaDepth > 8) throw new Error("HEVC sample bit depth is invalid.");
62
+ const orderBits = bits.ue() + 4;
63
+ if (orderBits > 16) throw new Error("HEVC picture-order width is invalid.");
64
+ const orderingPresent = bits.uint(1);
65
+ let reorderDepth = 0, buffering = 0;
66
+ for (let layer = orderingPresent ? 0 : layers; layer <= layers; layer++) {
67
+ const capacity = bits.ue() + 1, reorder = bits.ue(); bits.ue();
68
+ if (capacity > 16 || reorder >= capacity || capacity < buffering || reorder < reorderDepth) throw new Error("HEVC reorder-buffer declaration is invalid.");
69
+ buffering = capacity; reorderDepth = reorder;
70
+ }
71
+ const chromaArray = separate ? 0 : chroma;
72
+ const width = codedWidth - (left + right) * ([1, 2].includes(chromaArray) ? 2 : 1);
73
+ const height = codedHeight - (top + bottom) * (chromaArray === 1 ? 2 : 1);
74
+ if (!(width > 0) || !(height > 0)) throw new Error("HEVC conformance window removes the entire picture.");
75
+ return { width, height, bitDepth: luma + 8, chromaBitDepth: chromaDepth + 8, chromaFormat: chroma, separateColourPlane: separate === 1, reorderDepth, orderBits,
76
+ ...sequenceTiming(bits, orderBits) };
77
+ }
78
+
79
+ function skip(bits, count) { for (let left = count; left > 0; left -= 32) bits.uint(Math.min(32, left)); }
80
+
81
+ function sequenceTiming(bits, orderBits) {
82
+ for (let field = 0; field < 6; field++) bits.ue();
83
+ if (bits.uint(1) && bits.uint(1)) {
84
+ for (let size = 0; size < 4; size++) for (let matrix = 0; matrix < 6; matrix += size === 3 ? 3 : 1) {
85
+ if (!bits.uint(1)) bits.ue();
86
+ else {
87
+ if (size > 1) bits.se();
88
+ for (let coefficient = 0; coefficient < Math.min(64, 2 ** (4 + 2 * size)); coefficient++) bits.se();
89
+ }
90
+ }
91
+ }
92
+ bits.uint(1); bits.uint(1);
93
+ if (bits.uint(1)) { bits.uint(8); bits.ue(); bits.ue(); bits.uint(1); }
94
+ const sets = bits.ue();
95
+ if (sets > 64) throw new Error("HEVC short-term reference count is invalid.");
96
+ const previous = [];
97
+ for (let set = 0; set < sets; set++) {
98
+ const deltas = [];
99
+ if (set > 0 && bits.uint(1)) {
100
+ const sign = bits.uint(1), delta = (bits.ue() + 1) * (sign ? -1 : 1);
101
+ for (const before of [...previous[set - 1], 0]) {
102
+ const used = bits.uint(1), retained = used || bits.uint(1);
103
+ if (retained && before + delta !== 0) deltas.push(before + delta);
104
+ }
105
+ } else {
106
+ const negative = bits.ue(), positive = bits.ue();
107
+ if (negative + positive > 16) throw new Error("HEVC reference picture set exceeds its buffer.");
108
+ let value = 0;
109
+ for (let pic = 0; pic < negative; pic++) { value -= bits.ue() + 1; bits.uint(1); deltas.push(value); }
110
+ value = 0;
111
+ for (let pic = 0; pic < positive; pic++) { value += bits.ue() + 1; bits.uint(1); deltas.push(value); }
112
+ }
113
+ if (deltas.length > 16) throw new Error("HEVC predicted reference set exceeds its buffer.");
114
+ previous.push([...deltas.filter(delta => delta < 0).sort((a, b) => b - a),
115
+ ...deltas.filter(delta => delta > 0).sort((a, b) => a - b)]);
116
+ }
117
+ if (bits.uint(1)) {
118
+ const count = bits.ue();
119
+ if (count > 32) throw new Error("HEVC long-term reference count is invalid.");
120
+ for (let pic = 0; pic < count; pic++) { bits.uint(orderBits); bits.uint(1); }
121
+ }
122
+ bits.uint(1); bits.uint(1);
123
+ if (!bits.uint(1)) return {};
124
+ if (bits.uint(1) && bits.uint(8) === 255) { bits.uint(16); bits.uint(16); }
125
+ if (bits.uint(1)) bits.uint(1);
126
+ if (bits.uint(1)) { bits.uint(4); if (bits.uint(1)) skip(bits, 24); }
127
+ if (bits.uint(1)) { bits.ue(); bits.ue(); }
128
+ bits.uint(1);
129
+ const fieldSequence = bits.uint(1), frameFieldInfo = bits.uint(1);
130
+ if (bits.uint(1)) for (let side = 0; side < 4; side++) bits.ue();
131
+ if (!bits.uint(1)) return { fieldSequence: fieldSequence === 1, frameFieldInfo: frameFieldInfo === 1 };
132
+ const units = bits.uint(32), scale = bits.uint(32);
133
+ const proportional = bits.uint(1), ticks = proportional ? bits.ue() + 1 : 1;
134
+ if (!(units > 0) || !(scale > 0)) throw new Error("HEVC timing declaration is invalid.");
135
+ return { timingTickSeconds: units / scale, fps: scale / (units * ticks),
136
+ fieldSequence: fieldSequence === 1, frameFieldInfo: frameFieldInfo === 1,
137
+ pocProportional: proportional === 1, ticksPerPicture: ticks };
138
+ }
139
+
140
+ /** Construct hvcC from complete in-band parameter sets without keeping picture bytes. */
141
+ export function hevcConfigurationFromUnits(vps, sps, pps) {
142
+ const facts = hevcSequence(sps);
143
+ const raw = [];
144
+ for (let at = 2, zeroes = 0; at < sps.length; at++) {
145
+ const byte = sps[at];
146
+ if (zeroes >= 2 && byte === 3) { zeroes = 0; continue; }
147
+ raw.push(byte); zeroes = byte === 0 ? zeroes + 1 : 0;
148
+ }
149
+ const header = Buffer.alloc(23);
150
+ header[0] = 1;
151
+ Buffer.from(raw).copy(header, 1, 1, 13);
152
+ header[13] = 0xf0; header[15] = 0xfc;
153
+ header[16] = 0xfc | facts.chromaFormat;
154
+ header[17] = 0xf8 | (facts.bitDepth - 8); header[18] = 0xf8 | (facts.chromaBitDepth - 8);
155
+ header[21] = ((((raw[0] >> 1) & 7) + 1) << 3) | ((raw[0] & 1) << 2) | 3;
156
+ header[22] = 3;
157
+ const arrays = [vps, sps, pps].map((unit, index) => {
158
+ if (!Buffer.isBuffer(unit) || unit.length < 2 || unit.length > 65535 || ((unit[0] >> 1) & 63) !== index + 32) {
159
+ throw new Error("HEVC parameter-set declaration is invalid.");
160
+ }
161
+ return Buffer.concat([Buffer.from([0x80 | (index + 32), 0, 1, unit.length >> 8, unit.length & 255]), unit]);
162
+ });
163
+ const bytes = Buffer.concat([header, ...arrays]);
164
+ return { ...facts, codecPrivateB64: bytes.toString("base64"), packetFraming: "annex-b" };
165
+ }
@@ -0,0 +1,132 @@
1
+ import { hevcConfigurationFromUnits } from "./hevc-configuration.js";
2
+ import { HevcPictureOrder } from "./hevc-picture-order.js";
3
+ import { CadencedPictureOrder } from "./CadencedPictureOrder.js";
4
+
5
+ /** HEVC access units keep exact source addresses and the PES presentation/decode clocks. */
6
+ export class HevcElementaryIndex {
7
+ #track;
8
+ #index;
9
+ #position = 0;
10
+ #zeroes = 0;
11
+ #nal = null;
12
+ #mappings = [];
13
+ #stamps = [];
14
+ #sets = new Map();
15
+ #frame = null;
16
+ #prefix = 0;
17
+ #recent = [];
18
+ #order = null;
19
+ #anchor = null;
20
+ #cadenced = null;
21
+
22
+ constructor(track, index) {
23
+ this.#track = track; this.#index = index;
24
+ if (track.presentationCadenceSeconds > 0) this.#cadenced = new CadencedPictureOrder(track, index);
25
+ }
26
+
27
+ push(bytes, sourceStart, stamp) {
28
+ const from = this.#position;
29
+ this.#mappings.push({ from, to: from + bytes.length, sourceStart });
30
+ if (stamp?.pts !== null && stamp?.pts !== undefined) this.#stamps.push({ position: from, pts: stamp.pts, dts: stamp.dts ?? stamp.pts });
31
+ for (const byte of bytes) {
32
+ if (byte === 1 && this.#zeroes >= 2) {
33
+ const start = this.#position - this.#zeroes;
34
+ if (this.#nal) this.#finishNal(start);
35
+ this.#nal = { start, payloadStart: this.#position + 1, bytes: [], type: null };
36
+ } else if (this.#nal) {
37
+ if (this.#nal.type === null) {
38
+ if (byte & 128) throw new Error("HEVC NAL header is invalid.");
39
+ this.#nal.type = (byte >> 1) & 63;
40
+ }
41
+ const limit = [32, 33, 34].includes(this.#nal.type) ? 65535 : 64;
42
+ if (this.#nal.bytes.length < limit) this.#nal.bytes.push(byte);
43
+ else if ([32, 33, 34].includes(this.#nal.type)) throw new Error("HEVC parameter set exceeds its decoder declaration.");
44
+ } else if (byte !== 0) throw new Error("HEVC bytes precede their first start code.");
45
+ this.#zeroes = byte === 0 ? this.#zeroes + 1 : 0;
46
+ this.#position++;
47
+ }
48
+ }
49
+
50
+ complete() {
51
+ if (this.#nal) this.#finishNal(this.#position - this.#zeroes);
52
+ if (this.#frame) {
53
+ if (!(this.#track.fps > 0) || this.#track.fieldSequence) throw new Error("HEVC final frame timing is not declared.");
54
+ this.#close(this.#position - this.#zeroes, 1 / this.#track.fps);
55
+ }
56
+ this.#cadenced?.finish();
57
+ this.#index.complete(this.#track.trackNumber);
58
+ }
59
+
60
+ #finishNal(end) {
61
+ const nal = this.#nal;
62
+ if (end < nal.payloadStart + 2 || nal.bytes.length < 2) throw new Error("HEVC NAL unit is truncated.");
63
+ const bytes = Buffer.from(nal.bytes.slice(0, end - nal.payloadStart));
64
+ if (!(bytes[1] & 7) || (bytes[0] & 1) || (bytes[1] >> 3)) throw new Error("HEVC NAL temporal identity or layer is invalid.");
65
+ if ([32, 33, 34].includes(nal.type)) {
66
+ const previous = this.#sets.get(nal.type);
67
+ if (previous && !previous.equals(bytes)) throw new Error("HEVC parameter-set changes require packet-specific declarations.");
68
+ this.#sets.set(nal.type, bytes);
69
+ if ([32, 33, 34].every(type => this.#sets.has(type))) {
70
+ Object.assign(this.#track, hevcConfigurationFromUnits(this.#sets.get(32), this.#sets.get(33), this.#sets.get(34)));
71
+ this.#order ??= new HevcPictureOrder(this.#track, this.#sets.get(34));
72
+ }
73
+ }
74
+ if ([32, 33, 34, 35, 39].includes(nal.type) && this.#frame) {
75
+ if (this.#prefix === null) this.#prefix = nal.start;
76
+ return;
77
+ }
78
+ if (nal.type > 31) return;
79
+ if (bytes.length < 3) throw new Error("HEVC slice header is truncated.");
80
+ const firstSlice = (bytes[2] & 128) !== 0;
81
+ if (!firstSlice) {
82
+ if (!this.#frame) throw new Error("HEVC access unit starts with a dependent slice.");
83
+ return;
84
+ }
85
+ if (!this.#track.codecPrivateB64) throw new Error("HEVC picture precedes its decoder configuration.");
86
+ const start = this.#prefix ?? nal.start;
87
+ let stamp = null;
88
+ while (this.#stamps[0]?.position <= nal.start) stamp = this.#stamps.shift();
89
+ let pictureOrder = null;
90
+ try { pictureOrder = this.#order?.read(bytes); }
91
+ catch (error) { if (!stamp) throw error; }
92
+ if (pictureOrder?.reset) this.#anchor = null;
93
+ if (stamp && pictureOrder) this.#anchor = { count: pictureOrder.count, pts: stamp.pts };
94
+ if (this.#cadenced) {
95
+ if (!pictureOrder || this.#track.fieldSequence || this.#track.frameFieldInfo) throw new Error("Cadenced HEVC requires complete progressive picture order.");
96
+ stamp = { pts: 0, dts: this.#frame ? this.#frame.dts + this.#track.presentationCadenceSeconds : this.#track.startTimeSeconds };
97
+ }
98
+ if (!stamp) {
99
+ if (!pictureOrder || !this.#anchor || !this.#frame || !this.#track.pocProportional ||
100
+ this.#track.fieldSequence || this.#track.frameFieldInfo || !(this.#track.fps > 0)) {
101
+ throw new Error("HEVC access unit has neither a PES timestamp nor complete picture-order timing declarations.");
102
+ }
103
+ stamp = { pts: this.#anchor.pts + (pictureOrder.count - this.#anchor.count) / this.#track.fps,
104
+ dts: this.#frame.dts + 1 / this.#track.fps };
105
+ }
106
+ if (this.#frame) {
107
+ const duration = stamp.dts - this.#frame.dts;
108
+ if (!(duration > 0)) throw new Error("HEVC decode timestamps do not advance.");
109
+ this.#close(start, duration);
110
+ }
111
+ this.#frame = { start, pts: stamp.pts, dts: stamp.dts, keyframe: nal.type >= 16 && nal.type <= 21,
112
+ ...(this.#cadenced ? { pictureOrder: pictureOrder.count, orderReset: pictureOrder.reset } : {}) };
113
+ this.#prefix = null;
114
+ }
115
+
116
+ #close(end, duration) {
117
+ const { start, pictureOrder, orderReset, ...timing } = this.#frame;
118
+ const ranges = this.#mappings.filter(range => range.from < end && range.to > start).map(range => [
119
+ range.sourceStart + Math.max(start, range.from) - range.from,
120
+ range.sourceStart + Math.min(end, range.to) - range.from - 1]);
121
+ if (ranges.reduce((sum, [a, b]) => sum + b - a + 1, 0) !== end - start) throw new Error("HEVC access-unit addresses are incomplete.");
122
+ if (this.#cadenced) this.#cadenced.push({ ...timing, duration, ranges }, pictureOrder, orderReset);
123
+ else this.#index.append(this.#track.trackNumber, { ...timing, duration, ranges });
124
+ if (!this.#cadenced) this.#recent.push(timing.pts);
125
+ if (!this.#cadenced && this.#recent.length > this.#track.reorderDepth + 1) {
126
+ const earliest = Math.min(...this.#recent);
127
+ this.#recent.splice(this.#recent.indexOf(earliest), 1);
128
+ this.#index.coverThrough(this.#track.trackNumber, Math.max(0, earliest + duration));
129
+ }
130
+ this.#mappings = this.#mappings.filter(range => range.to > end);
131
+ }
132
+ }
@@ -0,0 +1,66 @@
1
+ import { Bits } from "./h264-configuration.js";
2
+
3
+ /** HEVC first-slice picture order, using the previous reference TemporalId 0 picture. */
4
+ export class HevcPictureOrder {
5
+ #sequence;
6
+ #picture;
7
+ #lsb = 0;
8
+ #msb = 0;
9
+ #first = true;
10
+
11
+ constructor(sequence, pps) {
12
+ if (!Number.isSafeInteger(sequence.orderBits) || sequence.orderBits < 4 || sequence.orderBits > 16 ||
13
+ pps.length < 3 || ((pps[0] >> 1) & 63) !== 34 || (pps[0] & 129) || (pps[1] >> 3) || !(pps[1] & 7)) {
14
+ throw new Error("HEVC picture-order declarations are invalid.");
15
+ }
16
+ this.#sequence = sequence;
17
+ const bits = new Bits(rbsp(pps), "HEVC");
18
+ const id = bits.ue(), sequenceId = bits.ue();
19
+ if (id > 63 || sequenceId > 15) throw new Error("HEVC PPS identity is invalid.");
20
+ const dependentSlices = bits.uint(1), outputFlag = bits.uint(1), extraBits = bits.uint(3);
21
+ this.#picture = { id, dependentSlices, outputFlag, extraBits };
22
+ }
23
+
24
+ read(nal) {
25
+ const type = (nal[0] >> 1) & 63, temporalId = (nal[1] & 7) - 1;
26
+ if (type > 31 || temporalId < 0 || (nal[0] & 129) || (nal[1] >> 3)) throw new Error("HEVC slice identity is invalid.");
27
+ const bits = new Bits(rbsp(nal), "HEVC");
28
+ if (!bits.uint(1)) throw new Error("HEVC picture order requires its first slice.");
29
+ const irap = type >= 16 && type <= 23;
30
+ if (irap) bits.uint(1);
31
+ if (bits.ue() !== this.#picture.id) throw new Error("HEVC slice names an undeclared PPS.");
32
+ bits.uint(this.#picture.extraBits);
33
+ if (bits.ue() > 2) throw new Error("HEVC slice type is invalid.");
34
+ if (this.#picture.outputFlag) bits.uint(1);
35
+ if (this.#sequence.separateColourPlane) bits.uint(2);
36
+ const idr = type === 19 || type === 20;
37
+ const lsb = idr ? 0 : bits.uint(this.#sequence.orderBits), cycle = 2 ** this.#sequence.orderBits;
38
+ const reset = idr || (type >= 16 && type <= 18) || (irap && this.#first);
39
+ let msb = reset ? 0 : this.#msb;
40
+ if (!reset) {
41
+ if (lsb < this.#lsb && this.#lsb - lsb >= cycle / 2) msb += cycle;
42
+ else if (lsb > this.#lsb && lsb - this.#lsb > cycle / 2) msb -= cycle;
43
+ }
44
+ if (temporalId === 0 && ![0, 2, 4, 6, 7, 8, 9].includes(type)) {
45
+ this.#lsb = lsb;
46
+ this.#msb = msb;
47
+ }
48
+ this.#first = false;
49
+ return { count: msb + lsb, reset };
50
+ }
51
+ }
52
+
53
+ function rbsp(nal) {
54
+ const bytes = [];
55
+ for (let at = 2, zeroes = 0; at < nal.length; at++) {
56
+ const byte = nal[at];
57
+ if (zeroes >= 2 && byte === 3) {
58
+ if (at + 1 >= nal.length || nal[at + 1] > 3) throw new Error("HEVC escape byte is invalid.");
59
+ zeroes = 0;
60
+ continue;
61
+ }
62
+ bytes.push(byte);
63
+ zeroes = byte === 0 ? zeroes + 1 : 0;
64
+ }
65
+ return Buffer.from(bytes);
66
+ }