avbridge 2.2.1 → 2.5.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 (165) hide show
  1. package/CHANGELOG.md +153 -1
  2. package/NOTICE.md +2 -2
  3. package/README.md +2 -3
  4. package/THIRD_PARTY_LICENSES.md +2 -2
  5. package/dist/avi-2JPBSHGA.js +183 -0
  6. package/dist/avi-2JPBSHGA.js.map +1 -0
  7. package/dist/avi-F6WZJK5T.cjs +185 -0
  8. package/dist/avi-F6WZJK5T.cjs.map +1 -0
  9. package/dist/{avi-GCGM7OJI.js → avi-NJXAXUXK.js} +9 -3
  10. package/dist/avi-NJXAXUXK.js.map +1 -0
  11. package/dist/{avi-6SJLWIWW.cjs → avi-W6L3BTWU.cjs} +10 -4
  12. package/dist/avi-W6L3BTWU.cjs.map +1 -0
  13. package/dist/chunk-2IJ66NTD.cjs +212 -0
  14. package/dist/chunk-2IJ66NTD.cjs.map +1 -0
  15. package/dist/{chunk-ILKDNBSE.js → chunk-2XW2O3YI.cjs} +55 -10
  16. package/dist/chunk-2XW2O3YI.cjs.map +1 -0
  17. package/dist/chunk-5KVLE6YI.js +167 -0
  18. package/dist/chunk-5KVLE6YI.js.map +1 -0
  19. package/dist/chunk-5YAWWKA3.js +18 -0
  20. package/dist/chunk-5YAWWKA3.js.map +1 -0
  21. package/dist/chunk-CPJLFFCC.js +189 -0
  22. package/dist/chunk-CPJLFFCC.js.map +1 -0
  23. package/dist/chunk-CPZ7PXAM.cjs +240 -0
  24. package/dist/chunk-CPZ7PXAM.cjs.map +1 -0
  25. package/dist/{chunk-WD2ZNQA7.js → chunk-DCSOQH2N.js} +7 -4
  26. package/dist/chunk-DCSOQH2N.js.map +1 -0
  27. package/dist/{chunk-HZLQNKFN.cjs → chunk-E76AMWI4.js} +40 -15
  28. package/dist/chunk-E76AMWI4.js.map +1 -0
  29. package/dist/chunk-F3LQJKXK.cjs +20 -0
  30. package/dist/chunk-F3LQJKXK.cjs.map +1 -0
  31. package/dist/chunk-IAYKFGFG.js +200 -0
  32. package/dist/chunk-IAYKFGFG.js.map +1 -0
  33. package/dist/{chunk-DMWARSEF.js → chunk-KY2GPCT7.js} +788 -697
  34. package/dist/chunk-KY2GPCT7.js.map +1 -0
  35. package/dist/chunk-LUFA47FP.js +19 -0
  36. package/dist/chunk-LUFA47FP.js.map +1 -0
  37. package/dist/chunk-NNVOHKXJ.cjs +204 -0
  38. package/dist/chunk-NNVOHKXJ.cjs.map +1 -0
  39. package/dist/chunk-Q2VUO52Z.cjs +374 -0
  40. package/dist/chunk-Q2VUO52Z.cjs.map +1 -0
  41. package/dist/chunk-QDJLQR53.cjs +22 -0
  42. package/dist/chunk-QDJLQR53.cjs.map +1 -0
  43. package/dist/chunk-S4WAZC2T.cjs +173 -0
  44. package/dist/chunk-S4WAZC2T.cjs.map +1 -0
  45. package/dist/chunk-SMH6IOP2.js +368 -0
  46. package/dist/chunk-SMH6IOP2.js.map +1 -0
  47. package/dist/chunk-SR3MPV4D.js +237 -0
  48. package/dist/chunk-SR3MPV4D.js.map +1 -0
  49. package/dist/{chunk-UF2N5L63.cjs → chunk-TBW26OPP.cjs} +800 -710
  50. package/dist/chunk-TBW26OPP.cjs.map +1 -0
  51. package/dist/chunk-X2K3GIWE.js +235 -0
  52. package/dist/chunk-X2K3GIWE.js.map +1 -0
  53. package/dist/{chunk-L4NPOJ36.cjs → chunk-Z33SBWL5.cjs} +7 -4
  54. package/dist/chunk-Z33SBWL5.cjs.map +1 -0
  55. package/dist/chunk-ZCUXHW55.cjs +242 -0
  56. package/dist/chunk-ZCUXHW55.cjs.map +1 -0
  57. package/dist/element-browser.js +1282 -503
  58. package/dist/element-browser.js.map +1 -1
  59. package/dist/element.cjs +59 -5
  60. package/dist/element.cjs.map +1 -1
  61. package/dist/element.d.cts +39 -1
  62. package/dist/element.d.ts +39 -1
  63. package/dist/element.js +58 -4
  64. package/dist/element.js.map +1 -1
  65. package/dist/index.cjs +605 -327
  66. package/dist/index.cjs.map +1 -1
  67. package/dist/index.d.cts +48 -4
  68. package/dist/index.d.ts +48 -4
  69. package/dist/index.js +528 -319
  70. package/dist/index.js.map +1 -1
  71. package/dist/libav-demux-H2GS46GH.cjs +27 -0
  72. package/dist/{libav-http-reader-NQJVY273.js.map → libav-demux-H2GS46GH.cjs.map} +1 -1
  73. package/dist/libav-demux-OWZ4T2YW.js +6 -0
  74. package/dist/{libav-http-reader-FPYDBMYK.cjs.map → libav-demux-OWZ4T2YW.js.map} +1 -1
  75. package/dist/libav-http-reader-AZLE7YFS.cjs +16 -0
  76. package/dist/libav-http-reader-AZLE7YFS.cjs.map +1 -0
  77. package/dist/libav-http-reader-WXG3Z7AI.js +3 -0
  78. package/dist/libav-http-reader-WXG3Z7AI.js.map +1 -0
  79. package/dist/{libav-import-GST2AMPL.cjs → libav-import-2ZVKV2E7.cjs} +2 -2
  80. package/dist/{libav-import-GST2AMPL.cjs.map → libav-import-2ZVKV2E7.cjs.map} +1 -1
  81. package/dist/{libav-import-2JURFHEW.js → libav-import-6MGLCXVQ.js} +2 -2
  82. package/dist/{libav-import-2JURFHEW.js.map → libav-import-6MGLCXVQ.js.map} +1 -1
  83. package/dist/{player-U2NPmFvA.d.cts → player-B6WB74RD.d.cts} +62 -3
  84. package/dist/{player-U2NPmFvA.d.ts → player-B6WB74RD.d.ts} +62 -3
  85. package/dist/player.cjs +5631 -0
  86. package/dist/player.cjs.map +1 -0
  87. package/dist/player.d.cts +699 -0
  88. package/dist/player.d.ts +699 -0
  89. package/dist/player.js +5629 -0
  90. package/dist/player.js.map +1 -0
  91. package/dist/remux-OBSMIENG.cjs +35 -0
  92. package/dist/remux-OBSMIENG.cjs.map +1 -0
  93. package/dist/remux-WBYIZBBX.js +10 -0
  94. package/dist/remux-WBYIZBBX.js.map +1 -0
  95. package/dist/source-4TZ6KMNV.js +4 -0
  96. package/dist/{source-FFZ7TW2B.js.map → source-4TZ6KMNV.js.map} +1 -1
  97. package/dist/source-7YLO6E7X.cjs +29 -0
  98. package/dist/{source-CN43EI7Z.cjs.map → source-7YLO6E7X.cjs.map} +1 -1
  99. package/dist/source-MTX5ELUZ.js +4 -0
  100. package/dist/source-MTX5ELUZ.js.map +1 -0
  101. package/dist/source-VFLXLOCN.cjs +29 -0
  102. package/dist/source-VFLXLOCN.cjs.map +1 -0
  103. package/dist/subtitles-4T74JRGT.js +4 -0
  104. package/dist/subtitles-4T74JRGT.js.map +1 -0
  105. package/dist/subtitles-QUH4LPI4.cjs +29 -0
  106. package/dist/subtitles-QUH4LPI4.cjs.map +1 -0
  107. package/dist/variant-routing-434STYAB.js +3 -0
  108. package/dist/{variant-routing-JOBWXYKD.js.map → variant-routing-434STYAB.js.map} +1 -1
  109. package/dist/variant-routing-HONNAA6R.cjs +12 -0
  110. package/dist/{variant-routing-GOHB2RZN.cjs.map → variant-routing-HONNAA6R.cjs.map} +1 -1
  111. package/package.json +9 -1
  112. package/src/classify/rules.ts +27 -5
  113. package/src/convert/remux.ts +9 -35
  114. package/src/convert/transcode-libav.ts +691 -0
  115. package/src/convert/transcode.ts +53 -12
  116. package/src/element/avbridge-player.ts +861 -0
  117. package/src/element/avbridge-video.ts +54 -0
  118. package/src/element/player-icons.ts +25 -0
  119. package/src/element/player-styles.ts +472 -0
  120. package/src/errors.ts +53 -0
  121. package/src/index.ts +23 -0
  122. package/src/player-element.ts +18 -0
  123. package/src/player.ts +118 -27
  124. package/src/plugins/builtin.ts +2 -2
  125. package/src/probe/avi.ts +4 -0
  126. package/src/probe/index.ts +40 -10
  127. package/src/strategies/fallback/audio-output.ts +31 -0
  128. package/src/strategies/fallback/decoder.ts +179 -175
  129. package/src/strategies/fallback/index.ts +48 -6
  130. package/src/strategies/fallback/libav-import.ts +9 -1
  131. package/src/strategies/fallback/variant-routing.ts +7 -13
  132. package/src/strategies/fallback/video-renderer.ts +231 -32
  133. package/src/strategies/hybrid/decoder.ts +219 -200
  134. package/src/strategies/hybrid/index.ts +48 -7
  135. package/src/strategies/native.ts +6 -3
  136. package/src/strategies/remux/index.ts +14 -2
  137. package/src/strategies/remux/mse.ts +12 -2
  138. package/src/strategies/remux/pipeline.ts +72 -12
  139. package/src/subtitles/index.ts +7 -3
  140. package/src/subtitles/render.ts +8 -0
  141. package/src/types.ts +53 -1
  142. package/src/util/libav-demux.ts +405 -0
  143. package/src/util/libav-http-reader.ts +5 -1
  144. package/src/util/source.ts +28 -8
  145. package/src/util/transport.ts +26 -0
  146. package/vendor/libav/avbridge/libav-6.8.8.0-avbridge.wasm.mjs +1 -1
  147. package/vendor/libav/avbridge/libav-6.8.8.0-avbridge.wasm.wasm +0 -0
  148. package/dist/avi-6SJLWIWW.cjs.map +0 -1
  149. package/dist/avi-GCGM7OJI.js.map +0 -1
  150. package/dist/chunk-DMWARSEF.js.map +0 -1
  151. package/dist/chunk-HZLQNKFN.cjs.map +0 -1
  152. package/dist/chunk-ILKDNBSE.js.map +0 -1
  153. package/dist/chunk-J5MCMN3S.js +0 -27
  154. package/dist/chunk-J5MCMN3S.js.map +0 -1
  155. package/dist/chunk-L4NPOJ36.cjs.map +0 -1
  156. package/dist/chunk-NZU7W256.cjs +0 -29
  157. package/dist/chunk-NZU7W256.cjs.map +0 -1
  158. package/dist/chunk-UF2N5L63.cjs.map +0 -1
  159. package/dist/chunk-WD2ZNQA7.js.map +0 -1
  160. package/dist/libav-http-reader-FPYDBMYK.cjs +0 -16
  161. package/dist/libav-http-reader-NQJVY273.js +0 -3
  162. package/dist/source-CN43EI7Z.cjs +0 -28
  163. package/dist/source-FFZ7TW2B.js +0 -3
  164. package/dist/variant-routing-GOHB2RZN.cjs +0 -12
  165. package/dist/variant-routing-JOBWXYKD.js +0 -3
@@ -0,0 +1,240 @@
1
+ 'use strict';
2
+
3
+ var chunkZ33SBWL5_cjs = require('./chunk-Z33SBWL5.cjs');
4
+ var chunkG4APZMCP_cjs = require('./chunk-G4APZMCP.cjs');
5
+ var chunkF3LQJKXK_cjs = require('./chunk-F3LQJKXK.cjs');
6
+
7
+ // src/util/libav-demux.ts
8
+ async function openLibavDemux(opts) {
9
+ const variant = opts.variant ?? chunkF3LQJKXK_cjs.pickLibavVariant(opts.context);
10
+ const libav = await chunkG4APZMCP_cjs.loadLibav(variant);
11
+ const inputHandle = await chunkZ33SBWL5_cjs.prepareLibavInput(
12
+ libav,
13
+ opts.filename,
14
+ opts.source,
15
+ opts.transport
16
+ );
17
+ const readPkt = await libav.av_packet_alloc();
18
+ const [fmtCtx, streams] = await libav.ff_init_demuxer_file(opts.filename);
19
+ const videoStream = streams.find((s) => s.codec_type === libav.AVMEDIA_TYPE_VIDEO) ?? null;
20
+ const audioStream = streams.find((s) => s.codec_type === libav.AVMEDIA_TYPE_AUDIO) ?? null;
21
+ let destroyed = false;
22
+ async function pump(cb) {
23
+ while (!destroyed) {
24
+ if (cb.signal?.aborted) return;
25
+ let readErr;
26
+ let packets;
27
+ try {
28
+ [readErr, packets] = await libav.ff_read_frame_multi(fmtCtx, readPkt, {
29
+ // 16 KB batch — chosen so each read produces a handful of
30
+ // packets, keeping downstream queues bounded. Same rationale
31
+ // as the hybrid/fallback pumps (see CLAUDE.md note).
32
+ limit: 16 * 1024
33
+ });
34
+ } catch (err) {
35
+ throw new Error(`libav-demux: ff_read_frame_multi failed: ${err.message}`);
36
+ }
37
+ if (destroyed || cb.signal?.aborted) return;
38
+ const videoPackets = videoStream ? packets[videoStream.index] : void 0;
39
+ const audioPackets = audioStream ? packets[audioStream.index] : void 0;
40
+ if (cb.onAudioPackets && audioPackets && audioPackets.length > 0) {
41
+ await cb.onAudioPackets(audioPackets);
42
+ }
43
+ if (destroyed || cb.signal?.aborted) return;
44
+ if (cb.onVideoPackets && videoPackets && videoPackets.length > 0) {
45
+ await cb.onVideoPackets(videoPackets);
46
+ }
47
+ if (readErr === libav.AVERROR_EOF) {
48
+ if (cb.onEof) await cb.onEof();
49
+ return;
50
+ }
51
+ if (readErr && readErr !== 0 && readErr !== -libav.EAGAIN) {
52
+ throw new Error(`libav-demux: ff_read_frame_multi returned ${readErr}`);
53
+ }
54
+ }
55
+ }
56
+ async function destroy() {
57
+ destroyed = true;
58
+ try {
59
+ await libav.av_packet_free?.(readPkt);
60
+ } catch {
61
+ }
62
+ try {
63
+ await libav.avformat_close_input_js(fmtCtx);
64
+ } catch {
65
+ }
66
+ try {
67
+ await inputHandle.detach();
68
+ } catch {
69
+ }
70
+ }
71
+ return {
72
+ libav,
73
+ fmtCtx,
74
+ streams,
75
+ videoStream,
76
+ audioStream,
77
+ transport: inputHandle.transport,
78
+ pump,
79
+ destroy
80
+ };
81
+ }
82
+ function sanitizePacketTimestamp(pkt, nextUs, fallbackTimeBase) {
83
+ const lo = pkt.pts ?? 0;
84
+ const hi = pkt.ptshi ?? 0;
85
+ const isInvalid = hi === -2147483648 && lo === 0 || !Number.isFinite(lo);
86
+ if (isInvalid) {
87
+ const us2 = nextUs();
88
+ pkt.pts = us2;
89
+ pkt.ptshi = 0;
90
+ pkt.time_base_num = 1;
91
+ pkt.time_base_den = 1e6;
92
+ return;
93
+ }
94
+ const tb = fallbackTimeBase ?? [1, 1e6];
95
+ const pts64 = hi * 4294967296 + lo;
96
+ const us = Math.round(pts64 * 1e6 * tb[0] / tb[1]);
97
+ if (Number.isFinite(us) && Math.abs(us) <= Number.MAX_SAFE_INTEGER) {
98
+ pkt.pts = us;
99
+ pkt.ptshi = us < 0 ? -1 : 0;
100
+ pkt.time_base_num = 1;
101
+ pkt.time_base_den = 1e6;
102
+ return;
103
+ }
104
+ const fallback = nextUs();
105
+ pkt.pts = fallback;
106
+ pkt.ptshi = 0;
107
+ pkt.time_base_num = 1;
108
+ pkt.time_base_den = 1e6;
109
+ }
110
+ var AV_SAMPLE_FMT_U8 = 0;
111
+ var AV_SAMPLE_FMT_S16 = 1;
112
+ var AV_SAMPLE_FMT_S32 = 2;
113
+ var AV_SAMPLE_FMT_FLT = 3;
114
+ var AV_SAMPLE_FMT_U8P = 5;
115
+ var AV_SAMPLE_FMT_S16P = 6;
116
+ var AV_SAMPLE_FMT_S32P = 7;
117
+ var AV_SAMPLE_FMT_FLTP = 8;
118
+ function libavFrameToInterleavedFloat32(frame) {
119
+ const channels = frame.channels ?? frame.ch_layout_nb_channels ?? 1;
120
+ const sampleRate = frame.sample_rate ?? 44100;
121
+ const nbSamples = frame.nb_samples ?? 0;
122
+ if (nbSamples === 0) return null;
123
+ const out = new Float32Array(nbSamples * channels);
124
+ switch (frame.format) {
125
+ case AV_SAMPLE_FMT_FLTP: {
126
+ const planes = ensurePlanes(frame.data, channels);
127
+ for (let ch = 0; ch < channels; ch++) {
128
+ const plane = asFloat32(planes[ch]);
129
+ for (let i = 0; i < nbSamples; i++) out[i * channels + ch] = plane[i];
130
+ }
131
+ return { data: out, channels, sampleRate };
132
+ }
133
+ case AV_SAMPLE_FMT_FLT: {
134
+ const flat = asFloat32(frame.data);
135
+ for (let i = 0; i < nbSamples * channels; i++) out[i] = flat[i];
136
+ return { data: out, channels, sampleRate };
137
+ }
138
+ case AV_SAMPLE_FMT_S16P: {
139
+ const planes = ensurePlanes(frame.data, channels);
140
+ for (let ch = 0; ch < channels; ch++) {
141
+ const plane = asInt16(planes[ch]);
142
+ for (let i = 0; i < nbSamples; i++) out[i * channels + ch] = plane[i] / 32768;
143
+ }
144
+ return { data: out, channels, sampleRate };
145
+ }
146
+ case AV_SAMPLE_FMT_S16: {
147
+ const flat = asInt16(frame.data);
148
+ for (let i = 0; i < nbSamples * channels; i++) out[i] = flat[i] / 32768;
149
+ return { data: out, channels, sampleRate };
150
+ }
151
+ case AV_SAMPLE_FMT_S32P: {
152
+ const planes = ensurePlanes(frame.data, channels);
153
+ for (let ch = 0; ch < channels; ch++) {
154
+ const plane = asInt32(planes[ch]);
155
+ for (let i = 0; i < nbSamples; i++) out[i * channels + ch] = plane[i] / 2147483648;
156
+ }
157
+ return { data: out, channels, sampleRate };
158
+ }
159
+ case AV_SAMPLE_FMT_S32: {
160
+ const flat = asInt32(frame.data);
161
+ for (let i = 0; i < nbSamples * channels; i++) out[i] = flat[i] / 2147483648;
162
+ return { data: out, channels, sampleRate };
163
+ }
164
+ case AV_SAMPLE_FMT_U8P: {
165
+ const planes = ensurePlanes(frame.data, channels);
166
+ for (let ch = 0; ch < channels; ch++) {
167
+ const plane = asUint8(planes[ch]);
168
+ for (let i = 0; i < nbSamples; i++) out[i * channels + ch] = (plane[i] - 128) / 128;
169
+ }
170
+ return { data: out, channels, sampleRate };
171
+ }
172
+ case AV_SAMPLE_FMT_U8: {
173
+ const flat = asUint8(frame.data);
174
+ for (let i = 0; i < nbSamples * channels; i++) out[i] = (flat[i] - 128) / 128;
175
+ return { data: out, channels, sampleRate };
176
+ }
177
+ default:
178
+ return null;
179
+ }
180
+ }
181
+ function ensurePlanes(data, channels) {
182
+ if (Array.isArray(data)) return data;
183
+ const arr = data;
184
+ const len = arr.length;
185
+ const perChannel = Math.floor(len / channels);
186
+ const planes = [];
187
+ for (let ch = 0; ch < channels; ch++) {
188
+ planes.push(arr.subarray ? arr.subarray(ch * perChannel, (ch + 1) * perChannel) : arr);
189
+ }
190
+ return planes;
191
+ }
192
+ function asFloat32(x) {
193
+ if (x instanceof Float32Array) return x;
194
+ const ta = x;
195
+ return new Float32Array(ta.buffer, ta.byteOffset, ta.byteLength / 4);
196
+ }
197
+ function asInt16(x) {
198
+ if (x instanceof Int16Array) return x;
199
+ const ta = x;
200
+ return new Int16Array(ta.buffer, ta.byteOffset, ta.byteLength / 2);
201
+ }
202
+ function asInt32(x) {
203
+ if (x instanceof Int32Array) return x;
204
+ const ta = x;
205
+ return new Int32Array(ta.buffer, ta.byteOffset, ta.byteLength / 4);
206
+ }
207
+ function asUint8(x) {
208
+ if (x instanceof Uint8Array) return x;
209
+ const ta = x;
210
+ return new Uint8Array(ta.buffer, ta.byteOffset, ta.byteLength);
211
+ }
212
+ function sanitizeFrameTimestamp(frame, nextUs, fallbackTimeBase) {
213
+ const lo = frame.pts ?? 0;
214
+ const hi = frame.ptshi ?? 0;
215
+ const isInvalid = hi === -2147483648 && lo === 0 || !Number.isFinite(lo);
216
+ if (isInvalid) {
217
+ const us2 = nextUs();
218
+ frame.pts = us2;
219
+ frame.ptshi = 0;
220
+ return;
221
+ }
222
+ const tb = fallbackTimeBase ?? [1, 1e6];
223
+ const pts64 = hi * 4294967296 + lo;
224
+ const us = Math.round(pts64 * 1e6 * tb[0] / tb[1]);
225
+ if (Number.isFinite(us) && Math.abs(us) <= Number.MAX_SAFE_INTEGER) {
226
+ frame.pts = us;
227
+ frame.ptshi = us < 0 ? -1 : 0;
228
+ return;
229
+ }
230
+ const fallback = nextUs();
231
+ frame.pts = fallback;
232
+ frame.ptshi = 0;
233
+ }
234
+
235
+ exports.libavFrameToInterleavedFloat32 = libavFrameToInterleavedFloat32;
236
+ exports.openLibavDemux = openLibavDemux;
237
+ exports.sanitizeFrameTimestamp = sanitizeFrameTimestamp;
238
+ exports.sanitizePacketTimestamp = sanitizePacketTimestamp;
239
+ //# sourceMappingURL=chunk-CPZ7PXAM.cjs.map
240
+ //# sourceMappingURL=chunk-CPZ7PXAM.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/util/libav-demux.ts"],"names":["pickLibavVariant","loadLibav","prepareLibavInput","us"],"mappings":"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","file":"chunk-CPZ7PXAM.cjs","sourcesContent":["/**\n * Shared libav demux session. Opens a libav demuxer over a NormalizedSource\n * and provides a linear, cancellable packet pump.\n *\n * Phase 1 API: deliberately minimal. The first consumer is the AVI/ASF/FLV\n * transcode path (src/convert/transcode-libav.ts), which is strictly linear.\n * No seek, no track swapping — those were added to hybrid/fallback's\n * private pumps for playback reasons. When those paths migrate here, the\n * API will grow to cover their needs.\n *\n * The shared timestamp sanitizers (sanitizePacketTimestamp,\n * sanitizeFrameTimestamp) also live here. They were previously duplicated\n * in convert/remux.ts and strategies/hybrid/decoder.ts. The duplicates\n * stay put in Phase 1 with TODO pointers; migration is a follow-up.\n */\n\nimport { loadLibav, type LibavVariant } from \"../strategies/fallback/libav-loader.js\";\nimport { pickLibavVariant } from \"../strategies/fallback/variant-routing.js\";\nimport { prepareLibavInput } from \"./libav-http-reader.js\";\nimport type { MediaContext, TransportConfig } from \"../types.js\";\nimport type { NormalizedSource } from \"./source.js\";\n\n// ─────────────────────────────────────────────────────────────────────────\n// Structural types (mirror libav.js' shape without dragging in its types)\n// ─────────────────────────────────────────────────────────────────────────\n\nexport interface LibavStream {\n index: number;\n codec_type: number;\n codec_id: number;\n codecpar: number;\n time_base_num?: number;\n time_base_den?: number;\n}\n\nexport interface LibavPacket {\n data: Uint8Array;\n pts: number;\n ptshi?: number;\n duration?: number;\n durationhi?: number;\n flags: number;\n stream_index: number;\n time_base_num?: number;\n time_base_den?: number;\n}\n\nexport interface LibavFrame {\n data: unknown;\n format: number;\n channels?: number;\n ch_layout_nb_channels?: number;\n sample_rate?: number;\n nb_samples?: number;\n pts?: number;\n ptshi?: number;\n width?: number;\n height?: number;\n}\n\ninterface LibavRuntime {\n AVMEDIA_TYPE_VIDEO: number;\n AVMEDIA_TYPE_AUDIO: number;\n AVERROR_EOF: number;\n EAGAIN: number;\n\n mkreadaheadfile(name: string, blob: Blob): Promise<void>;\n unlinkreadaheadfile(name: string): Promise<void>;\n ff_init_demuxer_file(name: string): Promise<[number, LibavStream[]]>;\n ff_read_frame_multi(\n fmt_ctx: number,\n pkt: number,\n opts?: { limit?: number },\n ): Promise<[number, Record<number, LibavPacket[]>]>;\n av_packet_alloc(): Promise<number>;\n av_packet_free?(pkt: number): Promise<void>;\n avformat_close_input_js(ctx: number): Promise<void>;\n f64toi64?(val: number): [number, number];\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Session\n// ─────────────────────────────────────────────────────────────────────────\n\nexport interface LibavDemuxSession {\n readonly libav: LibavRuntime;\n readonly fmtCtx: number;\n readonly streams: LibavStream[];\n readonly videoStream: LibavStream | null;\n readonly audioStream: LibavStream | null;\n /** True when the input is being streamed via HTTP Range requests. */\n readonly transport: \"http-range\" | \"blob\";\n /**\n * Linear read-to-EOF pump. Invokes the callbacks for each\n * ff_read_frame_multi batch (audio is handed over before video per\n * batch, matching the audio-first ordering that the hybrid/fallback\n * playback pumps use — see POSTMORTEMS.md entry 1).\n *\n * Honors the AbortSignal between batches. Invokes `onEof` once when\n * the demuxer returns EOF. Does NOT handle seek.\n */\n pump(cb: {\n onVideoPackets?: (pkts: LibavPacket[]) => Promise<void>;\n onAudioPackets?: (pkts: LibavPacket[]) => Promise<void>;\n onEof?: () => Promise<void>;\n signal?: AbortSignal;\n }): Promise<void>;\n destroy(): Promise<void>;\n}\n\nexport interface OpenLibavDemuxOptions {\n source: NormalizedSource;\n filename: string;\n context: MediaContext;\n transport?: TransportConfig;\n /** Override automatic variant picking. Defaults to pickLibavVariant(context). */\n variant?: LibavVariant;\n}\n\nexport async function openLibavDemux(opts: OpenLibavDemuxOptions): Promise<LibavDemuxSession> {\n const variant: LibavVariant = opts.variant ?? pickLibavVariant(opts.context);\n const libav = (await loadLibav(variant)) as unknown as LibavRuntime;\n\n const inputHandle = await prepareLibavInput(\n libav as unknown as Parameters<typeof prepareLibavInput>[0],\n opts.filename,\n opts.source,\n opts.transport,\n );\n\n const readPkt = await libav.av_packet_alloc();\n const [fmtCtx, streams] = await libav.ff_init_demuxer_file(opts.filename);\n const videoStream = streams.find((s) => s.codec_type === libav.AVMEDIA_TYPE_VIDEO) ?? null;\n const audioStream = streams.find((s) => s.codec_type === libav.AVMEDIA_TYPE_AUDIO) ?? null;\n\n let destroyed = false;\n\n async function pump(cb: Parameters<LibavDemuxSession[\"pump\"]>[0]): Promise<void> {\n while (!destroyed) {\n if (cb.signal?.aborted) return;\n\n let readErr: number;\n let packets: Record<number, LibavPacket[]>;\n try {\n [readErr, packets] = await libav.ff_read_frame_multi(fmtCtx, readPkt, {\n // 16 KB batch — chosen so each read produces a handful of\n // packets, keeping downstream queues bounded. Same rationale\n // as the hybrid/fallback pumps (see CLAUDE.md note).\n limit: 16 * 1024,\n });\n } catch (err) {\n throw new Error(`libav-demux: ff_read_frame_multi failed: ${(err as Error).message}`);\n }\n\n if (destroyed || cb.signal?.aborted) return;\n\n const videoPackets = videoStream ? packets[videoStream.index] : undefined;\n const audioPackets = audioStream ? packets[audioStream.index] : undefined;\n\n // Audio-first ordering. Audio decode is cheap; video decode can\n // be expensive. Feeding audio first ensures the audio consumer\n // has samples to work with before any long video-decode block.\n if (cb.onAudioPackets && audioPackets && audioPackets.length > 0) {\n await cb.onAudioPackets(audioPackets);\n }\n if (destroyed || cb.signal?.aborted) return;\n if (cb.onVideoPackets && videoPackets && videoPackets.length > 0) {\n await cb.onVideoPackets(videoPackets);\n }\n\n if (readErr === libav.AVERROR_EOF) {\n if (cb.onEof) await cb.onEof();\n return;\n }\n if (readErr && readErr !== 0 && readErr !== -libav.EAGAIN) {\n throw new Error(`libav-demux: ff_read_frame_multi returned ${readErr}`);\n }\n }\n }\n\n async function destroy(): Promise<void> {\n destroyed = true;\n try { await libav.av_packet_free?.(readPkt); } catch { /* ignore */ }\n try { await libav.avformat_close_input_js(fmtCtx); } catch { /* ignore */ }\n try { await inputHandle.detach(); } catch { /* ignore */ }\n }\n\n return {\n libav,\n fmtCtx,\n streams,\n videoStream,\n audioStream,\n transport: inputHandle.transport,\n pump,\n destroy,\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Timestamp sanitizers (extracted from convert/remux.ts + hybrid/decoder.ts)\n//\n// libav can hand us packets/frames with pts = AV_NOPTS_VALUE (encoded as\n// ptshi = -2147483648, pts = 0) for inputs whose demuxer can't determine\n// presentation times. AVI is the canonical example. Downstream consumers\n// that treat pts as int64 overflow and throw.\n//\n// The sanitizer replaces invalid pts with a synthetic microsecond counter,\n// and normalizes valid pts to a 1/1e6 time_base so consumers don't need\n// to track the source time_base per packet.\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Sanitize a libav packet's timestamp. Mutates `pkt` in place.\n * If the packet has AV_NOPTS_VALUE, replaces pts with `nextUs()`.\n * Otherwise normalizes to µs with time_base = 1/1_000_000.\n */\nexport function sanitizePacketTimestamp(\n pkt: LibavPacket,\n nextUs: () => number,\n fallbackTimeBase?: [number, number],\n): void {\n const lo = pkt.pts ?? 0;\n const hi = pkt.ptshi ?? 0;\n const isInvalid = (hi === -2147483648 && lo === 0) || !Number.isFinite(lo);\n if (isInvalid) {\n const us = nextUs();\n pkt.pts = us;\n pkt.ptshi = 0;\n pkt.time_base_num = 1;\n pkt.time_base_den = 1_000_000;\n return;\n }\n const tb = fallbackTimeBase ?? [1, 1_000_000];\n const pts64 = hi * 0x100000000 + lo;\n const us = Math.round((pts64 * 1_000_000 * tb[0]) / tb[1]);\n if (Number.isFinite(us) && Math.abs(us) <= Number.MAX_SAFE_INTEGER) {\n pkt.pts = us;\n pkt.ptshi = us < 0 ? -1 : 0;\n pkt.time_base_num = 1;\n pkt.time_base_den = 1_000_000;\n return;\n }\n const fallback = nextUs();\n pkt.pts = fallback;\n pkt.ptshi = 0;\n pkt.time_base_num = 1;\n pkt.time_base_den = 1_000_000;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Audio frame → interleaved Float32 (extracted from\n// strategies/hybrid/decoder.ts + strategies/fallback/decoder.ts).\n//\n// libav hands us decoded audio frames in whichever sample format the codec\n// uses (FLTP, S16P, etc.). Most downstream consumers (Web Audio, WebCodecs\n// AudioEncoder) want interleaved Float32. This does the conversion without\n// any dependencies.\n// ─────────────────────────────────────────────────────────────────────────\n\nconst AV_SAMPLE_FMT_U8 = 0;\nconst AV_SAMPLE_FMT_S16 = 1;\nconst AV_SAMPLE_FMT_S32 = 2;\nconst AV_SAMPLE_FMT_FLT = 3;\nconst AV_SAMPLE_FMT_U8P = 5;\nconst AV_SAMPLE_FMT_S16P = 6;\nconst AV_SAMPLE_FMT_S32P = 7;\nconst AV_SAMPLE_FMT_FLTP = 8;\n\nexport interface InterleavedSamples {\n data: Float32Array;\n channels: number;\n sampleRate: number;\n}\n\nexport function libavFrameToInterleavedFloat32(frame: LibavFrame): InterleavedSamples | null {\n const channels = frame.channels ?? frame.ch_layout_nb_channels ?? 1;\n const sampleRate = frame.sample_rate ?? 44100;\n const nbSamples = frame.nb_samples ?? 0;\n if (nbSamples === 0) return null;\n\n const out = new Float32Array(nbSamples * channels);\n\n switch (frame.format) {\n case AV_SAMPLE_FMT_FLTP: {\n const planes = ensurePlanes(frame.data, channels);\n for (let ch = 0; ch < channels; ch++) {\n const plane = asFloat32(planes[ch]);\n for (let i = 0; i < nbSamples; i++) out[i * channels + ch] = plane[i];\n }\n return { data: out, channels, sampleRate };\n }\n case AV_SAMPLE_FMT_FLT: {\n const flat = asFloat32(frame.data);\n for (let i = 0; i < nbSamples * channels; i++) out[i] = flat[i];\n return { data: out, channels, sampleRate };\n }\n case AV_SAMPLE_FMT_S16P: {\n const planes = ensurePlanes(frame.data, channels);\n for (let ch = 0; ch < channels; ch++) {\n const plane = asInt16(planes[ch]);\n for (let i = 0; i < nbSamples; i++) out[i * channels + ch] = plane[i] / 32768;\n }\n return { data: out, channels, sampleRate };\n }\n case AV_SAMPLE_FMT_S16: {\n const flat = asInt16(frame.data);\n for (let i = 0; i < nbSamples * channels; i++) out[i] = flat[i] / 32768;\n return { data: out, channels, sampleRate };\n }\n case AV_SAMPLE_FMT_S32P: {\n const planes = ensurePlanes(frame.data, channels);\n for (let ch = 0; ch < channels; ch++) {\n const plane = asInt32(planes[ch]);\n for (let i = 0; i < nbSamples; i++) out[i * channels + ch] = plane[i] / 2147483648;\n }\n return { data: out, channels, sampleRate };\n }\n case AV_SAMPLE_FMT_S32: {\n const flat = asInt32(frame.data);\n for (let i = 0; i < nbSamples * channels; i++) out[i] = flat[i] / 2147483648;\n return { data: out, channels, sampleRate };\n }\n case AV_SAMPLE_FMT_U8P: {\n const planes = ensurePlanes(frame.data, channels);\n for (let ch = 0; ch < channels; ch++) {\n const plane = asUint8(planes[ch]);\n for (let i = 0; i < nbSamples; i++) out[i * channels + ch] = (plane[i] - 128) / 128;\n }\n return { data: out, channels, sampleRate };\n }\n case AV_SAMPLE_FMT_U8: {\n const flat = asUint8(frame.data);\n for (let i = 0; i < nbSamples * channels; i++) out[i] = (flat[i] - 128) / 128;\n return { data: out, channels, sampleRate };\n }\n default:\n return null;\n }\n}\n\nfunction ensurePlanes(data: unknown, channels: number): unknown[] {\n if (Array.isArray(data)) return data;\n const arr = data as { length: number; subarray?: (a: number, b: number) => unknown };\n const len = arr.length;\n const perChannel = Math.floor(len / channels);\n const planes: unknown[] = [];\n for (let ch = 0; ch < channels; ch++) {\n planes.push(arr.subarray ? arr.subarray(ch * perChannel, (ch + 1) * perChannel) : arr);\n }\n return planes;\n}\n\nfunction asFloat32(x: unknown): Float32Array {\n if (x instanceof Float32Array) return x;\n const ta = x as { buffer: ArrayBuffer; byteOffset: number; byteLength: number };\n return new Float32Array(ta.buffer, ta.byteOffset, ta.byteLength / 4);\n}\nfunction asInt16(x: unknown): Int16Array {\n if (x instanceof Int16Array) return x;\n const ta = x as { buffer: ArrayBuffer; byteOffset: number; byteLength: number };\n return new Int16Array(ta.buffer, ta.byteOffset, ta.byteLength / 2);\n}\nfunction asInt32(x: unknown): Int32Array {\n if (x instanceof Int32Array) return x;\n const ta = x as { buffer: ArrayBuffer; byteOffset: number; byteLength: number };\n return new Int32Array(ta.buffer, ta.byteOffset, ta.byteLength / 4);\n}\nfunction asUint8(x: unknown): Uint8Array {\n if (x instanceof Uint8Array) return x;\n const ta = x as { buffer: ArrayBuffer; byteOffset: number; byteLength: number };\n return new Uint8Array(ta.buffer, ta.byteOffset, ta.byteLength);\n}\n\n/**\n * Sanitize a decoded frame's timestamp. Mutates `frame` in place.\n * Returns nothing; callers that want derived metadata (e.g. a\n * VideoFrame timestamp in µs) should read `frame.pts` after calling.\n */\nexport function sanitizeFrameTimestamp(\n frame: LibavFrame,\n nextUs: () => number,\n fallbackTimeBase?: [number, number],\n): void {\n const lo = frame.pts ?? 0;\n const hi = frame.ptshi ?? 0;\n const isInvalid = (hi === -2147483648 && lo === 0) || !Number.isFinite(lo);\n if (isInvalid) {\n const us = nextUs();\n frame.pts = us;\n frame.ptshi = 0;\n return;\n }\n const tb = fallbackTimeBase ?? [1, 1_000_000];\n const pts64 = hi * 0x100000000 + lo;\n const us = Math.round((pts64 * 1_000_000 * tb[0]) / tb[1]);\n if (Number.isFinite(us) && Math.abs(us) <= Number.MAX_SAFE_INTEGER) {\n frame.pts = us;\n frame.ptshi = us < 0 ? -1 : 0;\n return;\n }\n const fallback = nextUs();\n frame.pts = fallback;\n frame.ptshi = 0;\n}\n"]}
@@ -1,9 +1,12 @@
1
1
  // src/util/libav-http-reader.ts
2
2
  var MIN_READ = 256 * 1024;
3
3
  var MAX_READ = 1 * 1024 * 1024;
4
- async function prepareLibavInput(libav, filename, source) {
4
+ async function prepareLibavInput(libav, filename, source, transport) {
5
5
  if (source.kind === "url") {
6
- const handle = await attachLibavHttpReader(libav, filename, source.url);
6
+ const handle = await attachLibavHttpReader(libav, filename, source.url, {
7
+ requestInit: transport?.requestInit,
8
+ fetchFn: transport?.fetchFn
9
+ });
7
10
  return {
8
11
  filename,
9
12
  transport: "http-range",
@@ -173,5 +176,5 @@ async function attachLibavHttpReader(libav, filename, url, options = {}) {
173
176
  }
174
177
 
175
178
  export { attachLibavHttpReader, prepareLibavInput };
176
- //# sourceMappingURL=chunk-WD2ZNQA7.js.map
177
- //# sourceMappingURL=chunk-WD2ZNQA7.js.map
179
+ //# sourceMappingURL=chunk-DCSOQH2N.js.map
180
+ //# sourceMappingURL=chunk-DCSOQH2N.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/util/libav-http-reader.ts"],"names":[],"mappings":"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","file":"chunk-DCSOQH2N.js","sourcesContent":["/**\n * libav.js HTTP block reader.\n *\n * Wraps `libav.mkblockreaderdev` + `libav.onblockread` +\n * `libav.ff_block_reader_dev_send` so that libav can demux a remote file\n * via HTTP Range requests instead of needing the entire file in memory.\n *\n * Used by the AVI/ASF/FLV probe path and the libav-backed playback /\n * conversion strategies whenever the source is a URL.\n *\n * Design notes:\n *\n * - **Range support detection** is done by issuing a `Range: bytes=0-0`\n * probe request. We do NOT trust `Accept-Ranges` headers — some servers\n * support ranges but don't advertise them, others advertise but don't.\n * The probe request is the canonical signal: a `206 Partial Content`\n * response means we can stream; anything else fails fast with a clear\n * error. We never silently fall back to a full download.\n *\n * - **Sequential reads.** libav can issue overlapping `onblockread`\n * callbacks. The reader serializes them through a single async queue\n * so a) `ff_block_reader_dev_send` calls are well-ordered and b) we\n * never have two in-flight fetches for unrelated reads. Throughput\n * for v1 is \"good enough\"; correctness > parallelism.\n *\n * - **In-flight dedup.** If libav asks for `(pos=1000, len=4096)` twice\n * in a row before the first request resolves, the second call awaits\n * the first instead of issuing a duplicate fetch. This handles the\n * \"demuxer re-reads the same header\" pattern cheaply.\n *\n * - **Read-ahead clamp.** libav's requested length is doubled, then\n * clamped to `[256 KB, 1 MB]`. Small reads get amortized; pathological\n * large requests don't OOM us.\n *\n * - **Last-block cache.** Only the most-recent fetched block is kept.\n * Re-fetches via Range are cheap; an LRU cache is post-1.0.\n *\n * - **Safe detach.** `detach()` clears `libav.onblockread`, sets a\n * destroyed flag, and ignores any in-flight fetch resolutions so we\n * never write into a torn-down demuxer.\n */\n\nconst MIN_READ = 256 * 1024;\nconst MAX_READ = 1 * 1024 * 1024;\n\ninterface LibavLike {\n mkblockreaderdev(name: string, size: number): Promise<void>;\n unlinkreadaheadfile(name: string): Promise<void>;\n ff_block_reader_dev_send(\n name: string,\n pos: number,\n data: Uint8Array | null,\n opts?: { errorCode?: number; error?: unknown },\n ): Promise<void>;\n onblockread?: (filename: string, pos: number, length: number) => void;\n}\n\nexport interface LibavHttpReaderHandle {\n /** Total file size (bytes) reported by the server. */\n readonly size: number;\n /** Always `\"http-range\"` for now. Reserved for future transports. */\n readonly transport: \"http-range\";\n /** Stop serving reads, clear the libav callback, and ignore late fetches. */\n detach(): Promise<void>;\n}\n\nexport interface AttachLibavHttpReaderOptions {\n /** Optional `RequestInit` extras (mode, credentials, headers, etc.). */\n requestInit?: RequestInit;\n /** Override fetch (for testing). Defaults to globalThis.fetch. */\n fetchFn?: typeof fetch;\n}\n\n/**\n * Result of preparing a libav-readable file from a normalized source.\n * Either an in-memory Blob (created via `mkreadaheadfile`) or a streaming\n * HTTP reader (created via `attachLibavHttpReader`). Callers should\n * `await detach()` when done so resources are cleaned up symmetrically.\n */\nexport interface LibavInputHandle {\n /** The virtual filename libav sees — pass to `ff_init_demuxer_file`. */\n readonly filename: string;\n /** \"blob\" for in-memory, \"http-range\" for streaming URL. */\n readonly transport: \"blob\" | \"http-range\";\n /** Total file size in bytes if known, otherwise undefined. */\n readonly size: number | undefined;\n /** Tear down the virtual file (and any HTTP reader state). */\n detach(): Promise<void>;\n}\n\ninterface LibavLikeWithBlob extends LibavLike {\n mkreadaheadfile(name: string, blob: Blob): Promise<void>;\n}\n\n/**\n * Convenience for the libav-backed strategies. Given a normalized source,\n * either creates an in-memory readahead file (for Blob inputs) or attaches\n * the HTTP block reader (for URL inputs). Returns a handle the caller\n * should detach when done.\n */\nexport async function prepareLibavInput(\n libav: LibavLikeWithBlob,\n filename: string,\n source: import(\"./source.js\").NormalizedSource,\n transport?: import(\"../types.js\").TransportConfig,\n): Promise<LibavInputHandle> {\n if (source.kind === \"url\") {\n const handle = await attachLibavHttpReader(libav, filename, source.url, {\n requestInit: transport?.requestInit,\n fetchFn: transport?.fetchFn,\n });\n return {\n filename,\n transport: \"http-range\",\n size: handle.size,\n detach: () => handle.detach(),\n };\n }\n await libav.mkreadaheadfile(filename, source.blob);\n return {\n filename,\n transport: \"blob\",\n size: source.byteLength,\n detach: async () => {\n try { await libav.unlinkreadaheadfile(filename); } catch { /* ignore */ }\n },\n };\n}\n\n/**\n * Attach an HTTP block reader to a libav.js instance. After this resolves,\n * libav can `ff_init_demuxer_file(filename)` and the demuxer will pull\n * bytes via Range requests instead of needing a Blob.\n *\n * Fails fast (before any libav setup) if the server doesn't support\n * Range requests.\n */\nexport async function attachLibavHttpReader(\n libav: LibavLike,\n filename: string,\n url: string,\n options: AttachLibavHttpReaderOptions = {},\n): Promise<LibavHttpReaderHandle> {\n const fetchFn = options.fetchFn ?? fetch;\n\n // 1. Probe the server with a single-byte Range request.\n let probeRes: Response;\n try {\n probeRes = await fetchFn(url, {\n ...options.requestInit,\n headers: {\n ...(options.requestInit?.headers ?? {}),\n Range: \"bytes=0-0\",\n },\n });\n } catch (err) {\n throw new Error(\n `libav HTTP reader: failed to reach ${url}: ${(err as Error).message}`,\n );\n }\n if (probeRes.status !== 206) {\n // 200 means the server ignored Range and would have sent the whole\n // file. We refuse to silently slurp gigabytes.\n throw new Error(\n `libav HTTP reader: ${url} does not support HTTP Range requests ` +\n `(server returned ${probeRes.status} for a Range probe; need 206 Partial Content). ` +\n `Remote AVI/ASF/FLV playback requires a server that honors byte-range requests.`,\n );\n }\n\n // 2. Parse total file size from Content-Range: \"bytes 0-0/12345678\"\n const contentRange = probeRes.headers.get(\"content-range\") ?? \"\";\n const sizeMatch = contentRange.match(/\\/(\\d+)$/);\n if (!sizeMatch) {\n throw new Error(\n `libav HTTP reader: ${url} returned 206 but no parseable Content-Range header (got: \"${contentRange}\")`,\n );\n }\n const size = parseInt(sizeMatch[1], 10);\n if (!Number.isFinite(size) || size <= 0) {\n throw new Error(\n `libav HTTP reader: ${url} reported invalid file size ${size}`,\n );\n }\n\n // Drain the probe body so the connection can be reused.\n try { await probeRes.arrayBuffer(); } catch { /* ignore */ }\n\n // 3. Create the virtual file libav will read from.\n await libav.mkblockreaderdev(filename, size);\n\n // ── State ───────────────────────────────────────────────────────────────\n\n let detached = false;\n // Most-recently fetched block. Cached so re-reads of the same region\n // (e.g. demuxer re-walks the header) don't issue another HTTP request.\n let cached: { pos: number; bytes: Uint8Array } | null = null;\n // The currently in-flight fetch, if any. Used both for serialization\n // (we await this before starting another) and for in-flight dedup.\n let inflight: Promise<void> | null = null;\n\n function clampReadLength(requested: number): number {\n const doubled = requested * 2;\n if (doubled < MIN_READ) return MIN_READ;\n if (doubled > MAX_READ) return MAX_READ;\n return doubled;\n }\n\n /** True if the cached block fully covers `[pos, pos+length)`. */\n function cacheCovers(pos: number, length: number): boolean {\n if (!cached) return false;\n return pos >= cached.pos && pos + length <= cached.pos + cached.bytes.byteLength;\n }\n\n /** Slice the requested window out of the cached block. */\n function sliceFromCache(pos: number, length: number): Uint8Array {\n if (!cached) throw new Error(\"sliceFromCache called with no cache\");\n const offset = pos - cached.pos;\n return cached.bytes.subarray(offset, offset + length);\n }\n\n /** Fetch one Range and update the cache. */\n async function fetchRange(pos: number, length: number): Promise<Uint8Array> {\n const end = Math.min(pos + length - 1, size - 1);\n const res = await fetchFn(url, {\n ...options.requestInit,\n headers: {\n ...(options.requestInit?.headers ?? {}),\n Range: `bytes=${pos}-${end}`,\n },\n });\n if (res.status !== 206 && res.status !== 200) {\n throw new Error(\n `libav HTTP reader: Range request bytes=${pos}-${end} returned ${res.status}`,\n );\n }\n const buf = new Uint8Array(await res.arrayBuffer());\n cached = { pos, bytes: buf };\n return buf;\n }\n\n /**\n * Handle a single libav read request. Serializes against any in-flight\n * read by chaining off `inflight`. Honors `detached` at every async\n * boundary so a torn-down reader never writes back into libav.\n */\n async function handleRead(name: string, pos: number, length: number): Promise<void> {\n // Wait for any preceding read to finish so we don't interleave.\n if (inflight) {\n try { await inflight; } catch { /* ignore — that read's own caller handled it */ }\n }\n if (detached) return;\n\n // Cache hit — reply directly without a network round-trip.\n if (cacheCovers(pos, length)) {\n const data = sliceFromCache(pos, length);\n try { await libav.ff_block_reader_dev_send(name, pos, data); } catch { /* ignore — libav may have torn down */ }\n return;\n }\n\n // Cache miss — fetch via Range. Read-ahead amortizes small reads.\n const fetchLen = clampReadLength(length);\n const fetched = (async () => {\n try {\n const buf = await fetchRange(pos, fetchLen);\n if (detached) return;\n // Slice exactly what libav asked for and send it back.\n const reply = buf.subarray(0, Math.min(length, buf.byteLength));\n try { await libav.ff_block_reader_dev_send(name, pos, reply); } catch { /* ignore */ }\n } catch (err) {\n if (detached) return;\n // Signal EOF + error code to libav so the demuxer surfaces it.\n try {\n await libav.ff_block_reader_dev_send(name, pos, null, {\n error: err,\n });\n } catch { /* ignore */ }\n }\n })();\n inflight = fetched;\n try { await fetched; } finally { if (inflight === fetched) inflight = null; }\n }\n\n // 4. Wire the callback. The signature accepts `(name, pos, length)` and\n // we hand it to handleRead which does all the work asynchronously.\n // Note: libav.js dispatches this synchronously from a worker message,\n // so we kick off handleRead but don't await — the queue inside handleRead\n // serializes things.\n const previousCallback = libav.onblockread;\n libav.onblockread = (name: string, pos: number, length: number) => {\n if (detached || name !== filename) {\n // Forward to any previous callback (e.g. another reader on the same\n // libav instance). This is rare in practice but cheap to support.\n previousCallback?.(name, pos, length);\n return;\n }\n void handleRead(name, pos, length);\n };\n\n return {\n size,\n transport: \"http-range\",\n async detach() {\n if (detached) return;\n detached = true;\n // Restore the previous callback (if any) so we don't break unrelated\n // readers on the same libav instance.\n libav.onblockread = previousCallback;\n // Wait for the last in-flight read to settle so we don't tear down\n // the virtual file while libav is still expecting a response.\n if (inflight) {\n try { await inflight; } catch { /* ignore */ }\n }\n // Drop the cache and unlink the virtual file.\n cached = null;\n try { await libav.unlinkreadaheadfile(filename); } catch { /* ignore */ }\n },\n };\n}\n"]}
@@ -1,4 +1,24 @@
1
- 'use strict';
1
+ import { fetchWith, mergeFetchInit } from './chunk-LUFA47FP.js';
2
+
3
+ // src/errors.ts
4
+ var AvbridgeError = class extends Error {
5
+ constructor(code, message, recovery, options) {
6
+ super(message, options);
7
+ this.code = code;
8
+ this.recovery = recovery;
9
+ }
10
+ code;
11
+ recovery;
12
+ name = "AvbridgeError";
13
+ };
14
+ var ERR_PROBE_FAILED = "ERR_AVBRIDGE_PROBE_FAILED";
15
+ var ERR_PROBE_UNKNOWN_CONTAINER = "ERR_AVBRIDGE_PROBE_UNKNOWN_CONTAINER";
16
+ var ERR_PROBE_FETCH_FAILED = "ERR_AVBRIDGE_PROBE_FETCH_FAILED";
17
+ var ERR_ALL_STRATEGIES_EXHAUSTED = "ERR_AVBRIDGE_ALL_STRATEGIES_EXHAUSTED";
18
+ var ERR_PLAYER_NOT_READY = "ERR_AVBRIDGE_PLAYER_NOT_READY";
19
+ var ERR_LIBAV_NOT_REACHABLE = "ERR_AVBRIDGE_LIBAV_NOT_REACHABLE";
20
+ var ERR_MSE_NOT_SUPPORTED = "ERR_AVBRIDGE_MSE_NOT_SUPPORTED";
21
+ var ERR_MSE_CODEC_NOT_SUPPORTED = "ERR_AVBRIDGE_MSE_CODEC_NOT_SUPPORTED";
2
22
 
3
23
  // src/util/source.ts
4
24
  var SNIFF_BYTES_NEEDED = 380;
@@ -6,7 +26,7 @@ var URL_SNIFF_RANGE_BYTES = 32 * 1024;
6
26
  function isInMemorySource(source) {
7
27
  return source.kind === "blob";
8
28
  }
9
- async function normalizeSource(source) {
29
+ async function normalizeSource(source, transport) {
10
30
  if (source instanceof File) {
11
31
  return {
12
32
  kind: "blob",
@@ -29,22 +49,31 @@ async function normalizeSource(source) {
29
49
  }
30
50
  if (typeof source === "string" || source instanceof URL) {
31
51
  const url = source instanceof URL ? source.toString() : source;
32
- return await fetchUrlForSniff(url, source);
52
+ return await fetchUrlForSniff(url, source, transport);
33
53
  }
34
54
  throw new TypeError("unsupported source type");
35
55
  }
36
- async function fetchUrlForSniff(url, originalSource) {
56
+ async function fetchUrlForSniff(url, originalSource, transport) {
37
57
  const name = url.split("/").pop()?.split("?")[0] ?? void 0;
58
+ const doFetch = fetchWith(transport);
38
59
  let res;
39
60
  try {
40
- res = await fetch(url, {
61
+ res = await doFetch(url, mergeFetchInit(transport?.requestInit, {
41
62
  headers: { Range: `bytes=0-${URL_SNIFF_RANGE_BYTES - 1}` }
42
- });
63
+ }));
43
64
  } catch (err) {
44
- throw new Error(`failed to fetch source ${url}: ${err.message}`);
65
+ throw new AvbridgeError(
66
+ ERR_PROBE_FETCH_FAILED,
67
+ `Failed to fetch source ${url}: ${err.message}`,
68
+ "Check that the URL is reachable and CORS is configured. If the source requires authentication, pass requestInit with credentials/headers."
69
+ );
45
70
  }
46
71
  if (!res.ok && res.status !== 206) {
47
- throw new Error(`failed to fetch source ${url}: ${res.status} ${res.statusText}`);
72
+ throw new AvbridgeError(
73
+ ERR_PROBE_FETCH_FAILED,
74
+ `Failed to fetch source ${url}: ${res.status} ${res.statusText}`,
75
+ res.status === 403 || res.status === 401 ? "The server rejected the request. Pass requestInit with the required Authorization header or credentials." : "Check that the URL is correct and the server is reachable."
76
+ );
48
77
  }
49
78
  let byteLength;
50
79
  const contentRange = res.headers.get("content-range");
@@ -147,10 +176,6 @@ async function readBlobBytes(blob, limit) {
147
176
  });
148
177
  }
149
178
 
150
- exports.isInMemorySource = isInMemorySource;
151
- exports.normalizeSource = normalizeSource;
152
- exports.sniffContainer = sniffContainer;
153
- exports.sniffContainerFromBytes = sniffContainerFromBytes;
154
- exports.sniffNormalizedSource = sniffNormalizedSource;
155
- //# sourceMappingURL=chunk-HZLQNKFN.cjs.map
156
- //# sourceMappingURL=chunk-HZLQNKFN.cjs.map
179
+ export { AvbridgeError, ERR_ALL_STRATEGIES_EXHAUSTED, ERR_LIBAV_NOT_REACHABLE, ERR_MSE_CODEC_NOT_SUPPORTED, ERR_MSE_NOT_SUPPORTED, ERR_PLAYER_NOT_READY, ERR_PROBE_FAILED, ERR_PROBE_UNKNOWN_CONTAINER, isInMemorySource, normalizeSource, sniffContainer, sniffContainerFromBytes, sniffNormalizedSource };
180
+ //# sourceMappingURL=chunk-E76AMWI4.js.map
181
+ //# sourceMappingURL=chunk-E76AMWI4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/util/source.ts"],"names":["sniffBytes"],"mappings":"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","file":"chunk-E76AMWI4.js","sourcesContent":["/**\n * Structured error with a machine-readable code and human-readable\n * recovery hint. Consumers can switch on `error.code` for programmatic\n * handling and show `error.recovery` in UI.\n *\n * All codes use the `ERR_AVBRIDGE_` prefix to avoid collisions.\n */\nexport class AvbridgeError extends Error {\n override name = \"AvbridgeError\";\n\n constructor(\n /** Machine-readable error code. */\n public readonly code: string,\n message: string,\n /** Human-readable recovery suggestion. */\n public readonly recovery?: string,\n options?: ErrorOptions,\n ) {\n super(message, options);\n }\n}\n\n// ── Error codes ────────────────────────────────────────────────────────\n\n// Probe\nexport const ERR_PROBE_FAILED = \"ERR_AVBRIDGE_PROBE_FAILED\";\nexport const ERR_PROBE_UNKNOWN_CONTAINER = \"ERR_AVBRIDGE_PROBE_UNKNOWN_CONTAINER\";\nexport const ERR_PROBE_FETCH_FAILED = \"ERR_AVBRIDGE_PROBE_FETCH_FAILED\";\n\n// Codec / strategy\nexport const ERR_CODEC_NOT_SUPPORTED = \"ERR_AVBRIDGE_CODEC_NOT_SUPPORTED\";\nexport const ERR_STRATEGY_FAILED = \"ERR_AVBRIDGE_STRATEGY_FAILED\";\nexport const ERR_ALL_STRATEGIES_EXHAUSTED = \"ERR_AVBRIDGE_ALL_STRATEGIES_EXHAUSTED\";\n\n// Player lifecycle\nexport const ERR_PLAYER_NOT_READY = \"ERR_AVBRIDGE_PLAYER_NOT_READY\";\n\n// Transport / network\nexport const ERR_RANGE_NOT_SUPPORTED = \"ERR_AVBRIDGE_RANGE_NOT_SUPPORTED\";\nexport const ERR_FETCH_FAILED = \"ERR_AVBRIDGE_FETCH_FAILED\";\n\n// libav\nexport const ERR_LIBAV_NOT_REACHABLE = \"ERR_AVBRIDGE_LIBAV_NOT_REACHABLE\";\n\n// MSE\nexport const ERR_MSE_NOT_SUPPORTED = \"ERR_AVBRIDGE_MSE_NOT_SUPPORTED\";\nexport const ERR_MSE_CODEC_NOT_SUPPORTED = \"ERR_AVBRIDGE_MSE_CODEC_NOT_SUPPORTED\";\n\n// Transcode\nexport const ERR_TRANSCODE_ABORTED = \"ERR_AVBRIDGE_TRANSCODE_ABORTED\";\nexport const ERR_TRANSCODE_UNSUPPORTED_COMBO = \"ERR_AVBRIDGE_TRANSCODE_UNSUPPORTED_COMBO\";\nexport const ERR_TRANSCODE_DECODE = \"ERR_AVBRIDGE_TRANSCODE_DECODE\";\nexport const ERR_CONTAINER_NOT_SUPPORTED = \"ERR_AVBRIDGE_CONTAINER_NOT_SUPPORTED\";\n","import type { ContainerKind, MediaInput, TransportConfig } from \"../types.js\";\nimport { mergeFetchInit, fetchWith } from \"./transport.js\";\nimport { AvbridgeError, ERR_PROBE_FETCH_FAILED } from \"../errors.js\";\n\n/**\n * Bytes needed by the sniffer to identify every container we recognize.\n * MPEG-TS needs the most: a sync byte at offset 0 *and* offset 188 (one TS\n * packet apart). Allow a little extra for the M2TS variant (offset 4/192).\n */\nconst SNIFF_BYTES_NEEDED = 380;\n\n/**\n * Bytes to fetch from a URL during the initial sniff. We grab a slightly\n * larger range than `SNIFF_BYTES_NEEDED` so the cache has some headroom for\n * the demuxer's first read after sniffing, in case it wants to look at\n * a few extra bytes (e.g. mp4 ftyp + first moov box).\n */\nconst URL_SNIFF_RANGE_BYTES = 32 * 1024;\n\n/**\n * `NormalizedSource` is a discriminated union: every consumer (probe,\n * strategies) decides what to do based on `kind`. URL sources are NOT\n * fetched eagerly; we only do a Range request for the first ~32 KB so the\n * sniffer has bytes to look at. The strategies are then handed the URL\n * directly so they can stream the rest via Range requests.\n *\n * For File / Blob / ArrayBuffer / Uint8Array sources, the bytes are\n * already in memory, so we wrap them as a `blob` variant.\n */\nexport type NormalizedSource =\n | {\n kind: \"blob\";\n blob: Blob;\n name?: string;\n byteLength: number;\n original: MediaInput;\n }\n | {\n kind: \"url\";\n url: string;\n /** Bytes pulled via Range request for the sniffer. NOT the full file. */\n sniffBytes: Uint8Array;\n name?: string;\n /** Total file size from Content-Length / Content-Range. May be undefined. */\n byteLength: number | undefined;\n original: MediaInput;\n };\n\n/** True if this source carries the entire file's bytes (vs. streaming). */\nexport function isInMemorySource(source: NormalizedSource): source is Extract<NormalizedSource, { kind: \"blob\" }> {\n return source.kind === \"blob\";\n}\n\n\n/**\n * Normalize a `MediaInput` for the probe + strategy layers. **Does not**\n * download URL sources in full — only fetches the first ~32 KB via a\n * Range request, which is enough for the sniffer to identify the\n * container. The strategies are then expected to stream the rest via\n * mediabunny's `UrlSource` (Range requests, prefetch, parallelism, cache).\n *\n * For non-URL inputs, the bytes are already in memory and we just wrap them.\n */\nexport async function normalizeSource(\n source: MediaInput,\n transport?: TransportConfig,\n): Promise<NormalizedSource> {\n if (source instanceof File) {\n return {\n kind: \"blob\",\n blob: source,\n name: source.name,\n byteLength: source.size,\n original: source,\n };\n }\n if (source instanceof Blob) {\n return { kind: \"blob\", blob: source, byteLength: source.size, original: source };\n }\n if (source instanceof ArrayBuffer) {\n const blob = new Blob([source]);\n return { kind: \"blob\", blob, byteLength: blob.size, original: source };\n }\n if (source instanceof Uint8Array) {\n const blob = new Blob([source as BlobPart]);\n return { kind: \"blob\", blob, byteLength: blob.size, original: source };\n }\n if (typeof source === \"string\" || source instanceof URL) {\n const url = source instanceof URL ? source.toString() : source;\n return await fetchUrlForSniff(url, source, transport);\n }\n throw new TypeError(\"unsupported source type\");\n}\n\n/**\n * Fetch the first ~32 KB of a URL via a Range request. Falls back to a\n * full GET if the server doesn't support range requests, but in that case\n * we only read the first 32 KB and abort the rest of the response so we\n * don't accidentally buffer a large file.\n */\nasync function fetchUrlForSniff(\n url: string,\n originalSource: MediaInput,\n transport?: TransportConfig,\n): Promise<NormalizedSource> {\n const name = url.split(\"/\").pop()?.split(\"?\")[0] ?? undefined;\n const doFetch = fetchWith(transport);\n\n // First attempt: Range request for the sniff window.\n let res: Response;\n try {\n res = await doFetch(url, mergeFetchInit(transport?.requestInit, {\n headers: { Range: `bytes=0-${URL_SNIFF_RANGE_BYTES - 1}` },\n })!);\n } catch (err) {\n throw new AvbridgeError(\n ERR_PROBE_FETCH_FAILED,\n `Failed to fetch source ${url}: ${(err as Error).message}`,\n \"Check that the URL is reachable and CORS is configured. If the source requires authentication, pass requestInit with credentials/headers.\",\n );\n }\n if (!res.ok && res.status !== 206) {\n throw new AvbridgeError(\n ERR_PROBE_FETCH_FAILED,\n `Failed to fetch source ${url}: ${res.status} ${res.statusText}`,\n res.status === 403 || res.status === 401\n ? \"The server rejected the request. Pass requestInit with the required Authorization header or credentials.\"\n : \"Check that the URL is correct and the server is reachable.\",\n );\n }\n\n // Determine the total file size from Content-Range (preferred) or Content-Length.\n let byteLength: number | undefined;\n const contentRange = res.headers.get(\"content-range\");\n if (contentRange) {\n // \"bytes 0-32767/12345678\" — parse the part after the slash\n const m = contentRange.match(/\\/(\\d+)$/);\n if (m) byteLength = parseInt(m[1], 10);\n }\n if (byteLength === undefined) {\n const cl = res.headers.get(\"content-length\");\n if (cl) {\n const n = parseInt(cl, 10);\n if (Number.isFinite(n)) {\n // If the server returned 200 (full body), Content-Length is the\n // FILE size. If 206 (partial), it's the chunk size — only use it\n // as a total if no Content-Range was present (server doesn't do\n // ranges) AND the full response is smaller than our sniff window.\n if (res.status === 200) byteLength = n;\n else if (res.status === 206 && !contentRange) byteLength = n;\n }\n }\n }\n\n // Read the sniff bytes. If the server ignored the Range header and is\n // streaming the full file, only read the first window and let the rest\n // be GC'd. We use a reader so we can stop early.\n const reader = res.body?.getReader();\n if (!reader) {\n // No streamed body (some test environments). Fall back to .arrayBuffer()\n // and slice — this might pull more than we wanted, but only for the\n // initial sniff, not the full file.\n const buf = new Uint8Array(await res.arrayBuffer());\n const sniffBytes = buf.slice(0, URL_SNIFF_RANGE_BYTES);\n return { kind: \"url\", url, sniffBytes, name, byteLength, original: originalSource };\n }\n\n const chunks: Uint8Array[] = [];\n let collected = 0;\n while (collected < URL_SNIFF_RANGE_BYTES) {\n const { done, value } = await reader.read();\n if (done) break;\n chunks.push(value);\n collected += value.byteLength;\n }\n // Cancel the response so we don't keep downloading.\n await reader.cancel().catch(() => { /* ignore */ });\n\n // Concatenate up to URL_SNIFF_RANGE_BYTES.\n const total = Math.min(collected, URL_SNIFF_RANGE_BYTES);\n const sniffBytes = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n if (offset >= total) break;\n const room = total - offset;\n sniffBytes.set(chunk.subarray(0, Math.min(chunk.byteLength, room)), offset);\n offset += chunk.byteLength;\n }\n\n return { kind: \"url\", url, sniffBytes, name, byteLength, original: originalSource };\n}\n\n/**\n * Identify the container family from a small byte buffer. Used by the\n * probe layer for both file (Blob → first 380 bytes) and URL (Range\n * request → first 32 KB) inputs.\n *\n * Sniffing intentionally does not trust file extensions.\n */\nexport function sniffContainerFromBytes(head: Uint8Array): ContainerKind {\n // MPEG-TS: sync byte 0x47 every 188 bytes. Verify at least two sync\n // bytes in the right places to avoid false positives. Some captures\n // start with a few junk bytes — also try offsets 4 and 192 (M2TS).\n if (head.length >= 376 && head[0] === 0x47 && head[188] === 0x47) {\n return \"mpegts\";\n }\n if (head.length >= 380 && head[4] === 0x47 && head[192] === 0x47) {\n return \"mpegts\"; // M2TS — 4-byte timestamp prefix per packet\n }\n // RIFF....AVI → AVI\n if (\n head[0] === 0x52 && head[1] === 0x49 && head[2] === 0x46 && head[3] === 0x46 &&\n head[8] === 0x41 && head[9] === 0x56 && head[10] === 0x49\n ) return \"avi\";\n // RIFF....WAVE → WAV\n if (\n head[0] === 0x52 && head[1] === 0x49 && head[2] === 0x46 && head[3] === 0x46 &&\n head[8] === 0x57 && head[9] === 0x41 && head[10] === 0x56 && head[11] === 0x45\n ) return \"wav\";\n // EBML start: 1A 45 DF A3 → MKV/WebM. Distinguish later via DocType.\n if (head[0] === 0x1a && head[1] === 0x45 && head[2] === 0xdf && head[3] === 0xa3) {\n return \"mkv\";\n }\n // ftyp at offset 4 → MP4 family\n if (head[4] === 0x66 && head[5] === 0x74 && head[6] === 0x79 && head[7] === 0x70) {\n // brand at bytes 8..11\n const brand = String.fromCharCode(head[8], head[9], head[10], head[11]);\n if (brand.startsWith(\"qt\")) return \"mov\";\n return \"mp4\";\n }\n // ASF / WMV: 30 26 B2 75 8E 66 CF 11\n if (\n head[0] === 0x30 && head[1] === 0x26 && head[2] === 0xb2 && head[3] === 0x75 &&\n head[4] === 0x8e && head[5] === 0x66 && head[6] === 0xcf && head[7] === 0x11\n ) return \"asf\";\n // FLV: 46 4C 56\n if (head[0] === 0x46 && head[1] === 0x4c && head[2] === 0x56) return \"flv\";\n // RealMedia (.rm / .rmvb): \".RMF\" — 2E 52 4D 46\n if (head[0] === 0x2e && head[1] === 0x52 && head[2] === 0x4d && head[3] === 0x46) {\n return \"rm\";\n }\n // OggS: 4F 67 67 53\n if (head[0] === 0x4f && head[1] === 0x67 && head[2] === 0x67 && head[3] === 0x53) return \"ogg\";\n // FLAC: 66 4C 61 43\n if (head[0] === 0x66 && head[1] === 0x4c && head[2] === 0x61 && head[3] === 0x43) return \"flac\";\n // ID3v2: 49 44 33 → MP3 (with id3)\n if (head[0] === 0x49 && head[1] === 0x44 && head[2] === 0x33) return \"mp3\";\n // MPEG audio frame sync: FF Fx\n if (head[0] === 0xff && (head[1] & 0xe0) === 0xe0) {\n // ADTS: FF F1 / FF F9\n if ((head[1] & 0xf6) === 0xf0) return \"adts\";\n return \"mp3\";\n }\n return \"unknown\";\n}\n\n/**\n * Convenience: sniff a `NormalizedSource` regardless of kind. For URL\n * sources, uses the pre-fetched `sniffBytes`. For blob sources, reads the\n * first 380 bytes.\n */\nexport async function sniffNormalizedSource(source: NormalizedSource): Promise<ContainerKind> {\n if (source.kind === \"url\") {\n return sniffContainerFromBytes(source.sniffBytes);\n }\n const buf = await readBlobBytes(source.blob, SNIFF_BYTES_NEEDED);\n return sniffContainerFromBytes(new Uint8Array(buf));\n}\n\n/**\n * Backwards-compatible wrapper for code that still passes a Blob directly.\n * Prefer `sniffNormalizedSource` going forward.\n */\nexport async function sniffContainer(blob: Blob): Promise<ContainerKind> {\n const buf = await readBlobBytes(blob, SNIFF_BYTES_NEEDED);\n return sniffContainerFromBytes(new Uint8Array(buf));\n}\n\n/**\n * Read up to `limit` bytes from a Blob. Tries `Blob.arrayBuffer()` first\n * (modern browsers), then falls back to `FileReader` (works under jsdom).\n */\nasync function readBlobBytes(blob: Blob, limit: number): Promise<ArrayBuffer> {\n const slice = blob.slice(0, limit);\n if (typeof (slice as Blob & { arrayBuffer?: () => Promise<ArrayBuffer> }).arrayBuffer === \"function\") {\n try {\n return await (slice as Blob & { arrayBuffer: () => Promise<ArrayBuffer> }).arrayBuffer();\n } catch {\n /* fall through to FileReader */\n }\n }\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as ArrayBuffer);\n reader.onerror = () => reject(reader.error ?? new Error(\"FileReader failed\"));\n reader.readAsArrayBuffer(slice);\n });\n}\n"]}
@@ -0,0 +1,20 @@
1
+ 'use strict';
2
+
3
+ // src/strategies/fallback/variant-routing.ts
4
+ var LEGACY_CONTAINERS = /* @__PURE__ */ new Set(["avi", "asf", "flv"]);
5
+ var WEBCODECS_AUDIO = /* @__PURE__ */ new Set(["aac", "mp3", "opus", "vorbis", "flac"]);
6
+ var WEBCODECS_VIDEO = /* @__PURE__ */ new Set(["h264", "h265", "vp8", "vp9", "av1"]);
7
+ function pickLibavVariant(ctx) {
8
+ if (LEGACY_CONTAINERS.has(ctx.container)) return "avbridge";
9
+ for (const v of ctx.videoTracks) {
10
+ if (!WEBCODECS_VIDEO.has(v.codec)) return "avbridge";
11
+ }
12
+ for (const a of ctx.audioTracks) {
13
+ if (!WEBCODECS_AUDIO.has(a.codec)) return "avbridge";
14
+ }
15
+ return "webcodecs";
16
+ }
17
+
18
+ exports.pickLibavVariant = pickLibavVariant;
19
+ //# sourceMappingURL=chunk-F3LQJKXK.cjs.map
20
+ //# sourceMappingURL=chunk-F3LQJKXK.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/strategies/fallback/variant-routing.ts"],"names":[],"mappings":";;;AAmBA,IAAM,oCAAoB,IAAI,GAAA,CAAI,CAAC,KAAA,EAAO,KAAA,EAAO,KAAK,CAAC,CAAA;AAIvD,IAAM,eAAA,uBAAsB,GAAA,CAAgB,CAAC,OAAO,KAAA,EAAO,MAAA,EAAQ,QAAA,EAAU,MAAM,CAAC,CAAA;AACpF,IAAM,eAAA,uBAAsB,GAAA,CAAgB,CAAC,QAAQ,MAAA,EAAQ,KAAA,EAAO,KAAA,EAAO,KAAK,CAAC,CAAA;AAE1E,SAAS,iBAAiB,GAAA,EAAiC;AAChE,EAAA,IAAI,iBAAA,CAAkB,GAAA,CAAI,GAAA,CAAI,SAAS,GAAG,OAAO,UAAA;AACjD,EAAA,KAAA,MAAW,CAAA,IAAK,IAAI,WAAA,EAAa;AAE/B,IAAA,IAAI,CAAC,eAAA,CAAgB,GAAA,CAAI,CAAA,CAAE,KAAK,GAAG,OAAO,UAAA;AAAA,EAC5C;AACA,EAAA,KAAA,MAAW,CAAA,IAAK,IAAI,WAAA,EAAa;AAC/B,IAAA,IAAI,CAAC,eAAA,CAAgB,GAAA,CAAI,CAAA,CAAE,KAAK,GAAG,OAAO,UAAA;AAAA,EAC5C;AACA,EAAA,OAAO,WAAA;AACT","file":"chunk-F3LQJKXK.cjs","sourcesContent":["import type { MediaContext, AudioCodec, VideoCodec } from \"../../types.js\";\nimport type { LibavVariant } from \"./libav-loader.js\";\n\n/**\n * Decide which libav.js variant to load for a given media context.\n *\n * - **webcodecs** (~5 MB, npm) — modern formats only, designed for the\n * WebCodecs bridge. Used when the codec is browser-supported and we just\n * need libav.js for demuxing or as a parser source.\n *\n * - **avbridge** (custom build, vendor/libav/) — has the AVI/ASF/FLV demuxers\n * and the legacy decoders (WMV3, MPEG-4 Part 2, VC-1, MS-MPEG4 v1/2/3,\n * AC-3, WMA*). Required for any of those formats; the npm variants ship\n * none of them.\n *\n * Rule: pick \"avbridge\" if either the container or any codec is one only the\n * custom build can handle. Otherwise pick \"webcodecs\".\n */\n\nconst LEGACY_CONTAINERS = new Set([\"avi\", \"asf\", \"flv\"]);\n\n/** Codecs the webcodecs variant can handle (native browser codecs only).\n * Anything not in these sets needs the custom avbridge variant. */\nconst WEBCODECS_AUDIO = new Set<AudioCodec>([\"aac\", \"mp3\", \"opus\", \"vorbis\", \"flac\"]);\nconst WEBCODECS_VIDEO = new Set<VideoCodec>([\"h264\", \"h265\", \"vp8\", \"vp9\", \"av1\"]);\n\nexport function pickLibavVariant(ctx: MediaContext): LibavVariant {\n if (LEGACY_CONTAINERS.has(ctx.container)) return \"avbridge\";\n for (const v of ctx.videoTracks) {\n // Any codec the webcodecs variant can't handle → need avbridge\n if (!WEBCODECS_VIDEO.has(v.codec)) return \"avbridge\";\n }\n for (const a of ctx.audioTracks) {\n if (!WEBCODECS_AUDIO.has(a.codec)) return \"avbridge\";\n }\n return \"webcodecs\";\n}\n"]}