avbridge 2.0.0 → 2.1.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.
- package/CHANGELOG.md +52 -1
- package/NOTICE.md +82 -0
- package/THIRD_PARTY_LICENSES.md +121 -0
- package/dist/{avi-M5B4SHRM.cjs → avi-GNTV5ZOH.cjs} +4 -4
- package/dist/{avi-M5B4SHRM.cjs.map → avi-GNTV5ZOH.cjs.map} +1 -1
- package/dist/{avi-POCGZ4JX.js → avi-V6HYQVR2.js} +3 -3
- package/dist/{avi-POCGZ4JX.js.map → avi-V6HYQVR2.js.map} +1 -1
- package/dist/{chunk-HZF5JDOO.js → chunk-CUQD23WO.js} +4 -4
- package/dist/{chunk-HZF5JDOO.js.map → chunk-CUQD23WO.js.map} +1 -1
- package/dist/{chunk-5ISVAODK.js → chunk-EJH67FXG.js} +30 -6
- package/dist/chunk-EJH67FXG.js.map +1 -0
- package/dist/{chunk-Z2FJ5TJC.cjs → chunk-JQH6D4OE.cjs} +31 -6
- package/dist/chunk-JQH6D4OE.cjs.map +1 -0
- package/dist/{chunk-TTV56KDB.cjs → chunk-O34444ID.cjs} +6 -6
- package/dist/{chunk-TTV56KDB.cjs.map → chunk-O34444ID.cjs.map} +1 -1
- package/dist/element-browser.js +33647 -0
- package/dist/element-browser.js.map +1 -0
- package/dist/element.cjs +3 -3
- package/dist/element.js +2 -2
- package/dist/index.cjs +16 -16
- package/dist/index.js +4 -4
- package/dist/libav-loader-6APXVNIV.cjs +12 -0
- package/dist/{libav-loader-ZHOERPHW.cjs.map → libav-loader-6APXVNIV.cjs.map} +1 -1
- package/dist/libav-loader-XKH2TKUW.js +3 -0
- package/dist/{libav-loader-KA2MAWLM.js.map → libav-loader-XKH2TKUW.js.map} +1 -1
- package/package.json +19 -7
- package/src/strategies/fallback/libav-loader.ts +49 -7
- package/src/stubs/node-fs-promises.ts +29 -0
- package/vendor/libav/README.md +139 -0
- package/vendor/libav/avbridge/libav-6.8.8.0-avbridge.wasm.mjs +688 -0
- package/vendor/libav/avbridge/libav-6.8.8.0-avbridge.wasm.wasm +0 -0
- package/vendor/libav/avbridge/libav-avbridge.mjs +1 -0
- package/vendor/libav/webcodecs/libav-6.8.8.0-webcodecs.wasm.mjs +747 -0
- package/vendor/libav/webcodecs/libav-6.8.8.0-webcodecs.wasm.wasm +0 -0
- package/vendor/libav/webcodecs/libav-webcodecs.mjs +1 -0
- package/vendor/libav/webcodecs/libav.types.d.ts +5222 -0
- package/dist/chunk-5ISVAODK.js.map +0 -1
- package/dist/chunk-Z2FJ5TJC.cjs.map +0 -1
- package/dist/libav-loader-KA2MAWLM.js +0 -3
- package/dist/libav-loader-ZHOERPHW.cjs +0 -12
package/dist/element.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkO34444ID_cjs = require('./chunk-O34444ID.cjs');
|
|
4
4
|
require('./chunk-Y5FYF5KG.cjs');
|
|
5
|
-
require('./chunk-
|
|
5
|
+
require('./chunk-JQH6D4OE.cjs');
|
|
6
6
|
require('./chunk-NZU7W256.cjs');
|
|
7
7
|
|
|
8
8
|
// src/element/avbridge-video.ts
|
|
@@ -222,7 +222,7 @@ var AvbridgeVideoElement = class extends HTMLElementCtor {
|
|
|
222
222
|
this._dispatch("loadstart", {});
|
|
223
223
|
let player;
|
|
224
224
|
try {
|
|
225
|
-
player = await
|
|
225
|
+
player = await chunkO34444ID_cjs.createPlayer({
|
|
226
226
|
source,
|
|
227
227
|
target: this._videoEl,
|
|
228
228
|
// Honor the consumer's preferred initial strategy. "auto" means
|
package/dist/element.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { createPlayer } from './chunk-
|
|
1
|
+
import { createPlayer } from './chunk-CUQD23WO.js';
|
|
2
2
|
import './chunk-PQTZS7OA.js';
|
|
3
|
-
import './chunk-
|
|
3
|
+
import './chunk-EJH67FXG.js';
|
|
4
4
|
import './chunk-J5MCMN3S.js';
|
|
5
5
|
|
|
6
6
|
// src/element/avbridge-video.ts
|
package/dist/index.cjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkO34444ID_cjs = require('./chunk-O34444ID.cjs');
|
|
4
4
|
var chunkY5FYF5KG_cjs = require('./chunk-Y5FYF5KG.cjs');
|
|
5
5
|
var chunkL4NPOJ36_cjs = require('./chunk-L4NPOJ36.cjs');
|
|
6
|
-
require('./chunk-
|
|
6
|
+
require('./chunk-JQH6D4OE.cjs');
|
|
7
7
|
require('./chunk-NZU7W256.cjs');
|
|
8
8
|
|
|
9
9
|
// src/convert/remux.ts
|
|
@@ -21,7 +21,7 @@ var MEDIABUNNY_CONTAINERS = /* @__PURE__ */ new Set([
|
|
|
21
21
|
async function remux(source, options = {}) {
|
|
22
22
|
const outputFormat = options.outputFormat ?? "mp4";
|
|
23
23
|
options.signal?.throwIfAborted();
|
|
24
|
-
const ctx = await
|
|
24
|
+
const ctx = await chunkO34444ID_cjs.probe(source);
|
|
25
25
|
options.signal?.throwIfAborted();
|
|
26
26
|
validateRemuxEligibility(ctx, options.strict ?? false);
|
|
27
27
|
if (MEDIABUNNY_CONTAINERS.has(ctx.container)) {
|
|
@@ -33,7 +33,7 @@ function validateRemuxEligibility(ctx, strict) {
|
|
|
33
33
|
const video = ctx.videoTracks[0];
|
|
34
34
|
const audio = ctx.audioTracks[0];
|
|
35
35
|
if (video) {
|
|
36
|
-
const mbCodec =
|
|
36
|
+
const mbCodec = chunkO34444ID_cjs.avbridgeVideoToMediabunny(video.codec);
|
|
37
37
|
if (!mbCodec) {
|
|
38
38
|
throw new Error(
|
|
39
39
|
`Cannot remux: video codec "${video.codec}" is not supported for remuxing. Use transcode() to re-encode to a modern codec.`
|
|
@@ -41,7 +41,7 @@ function validateRemuxEligibility(ctx, strict) {
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
if (audio) {
|
|
44
|
-
const mbCodec =
|
|
44
|
+
const mbCodec = chunkO34444ID_cjs.avbridgeAudioToMediabunny(audio.codec);
|
|
45
45
|
if (!mbCodec) {
|
|
46
46
|
throw new Error(
|
|
47
47
|
`Cannot remux: audio codec "${audio.codec}" is not supported for remuxing. Use transcode() to re-encode to a modern codec.`
|
|
@@ -60,7 +60,7 @@ function validateRemuxEligibility(ctx, strict) {
|
|
|
60
60
|
async function remuxViaMediAbunny(ctx, outputFormat, options) {
|
|
61
61
|
const mb = await import('mediabunny');
|
|
62
62
|
const input = new mb.Input({
|
|
63
|
-
source: await
|
|
63
|
+
source: await chunkO34444ID_cjs.buildMediabunnySourceFromInput(mb, ctx.source),
|
|
64
64
|
formats: mb.ALL_FORMATS
|
|
65
65
|
});
|
|
66
66
|
const target = new mb.BufferTarget();
|
|
@@ -117,7 +117,7 @@ async function remuxViaLibav(ctx, outputFormat, options) {
|
|
|
117
117
|
let loadLibav;
|
|
118
118
|
let pickLibavVariant;
|
|
119
119
|
try {
|
|
120
|
-
const loader = await import('./libav-loader-
|
|
120
|
+
const loader = await import('./libav-loader-6APXVNIV.cjs');
|
|
121
121
|
const routing = await import('./variant-routing-GOHB2RZN.cjs');
|
|
122
122
|
loadLibav = loader.loadLibav;
|
|
123
123
|
pickLibavVariant = routing.pickLibavVariant;
|
|
@@ -146,8 +146,8 @@ async function doLibavRemux(libav, filename, ctx, outputFormat, options) {
|
|
|
146
146
|
const audioStream = streams.find((s) => s.codec_type === libav.AVMEDIA_TYPE_AUDIO) ?? null;
|
|
147
147
|
const videoTrackInfo = ctx.videoTracks[0];
|
|
148
148
|
const audioTrackInfo = ctx.audioTracks[0];
|
|
149
|
-
const mbVideoCodec = videoTrackInfo ?
|
|
150
|
-
const mbAudioCodec = audioTrackInfo ?
|
|
149
|
+
const mbVideoCodec = videoTrackInfo ? chunkO34444ID_cjs.avbridgeVideoToMediabunny(videoTrackInfo.codec) : null;
|
|
150
|
+
const mbAudioCodec = audioTrackInfo ? chunkO34444ID_cjs.avbridgeAudioToMediabunny(audioTrackInfo.codec) : null;
|
|
151
151
|
const target = new mb.BufferTarget();
|
|
152
152
|
const output = new mb.Output({
|
|
153
153
|
format: createOutputFormat(mb, outputFormat),
|
|
@@ -358,7 +358,7 @@ async function transcode(source, options = {}) {
|
|
|
358
358
|
const quality = options.quality ?? "medium";
|
|
359
359
|
validateCodecCompatibility(outputFormat, videoCodec, audioCodec);
|
|
360
360
|
options.signal?.throwIfAborted();
|
|
361
|
-
const ctx = await
|
|
361
|
+
const ctx = await chunkO34444ID_cjs.probe(source);
|
|
362
362
|
options.signal?.throwIfAborted();
|
|
363
363
|
if (!MEDIABUNNY_CONTAINERS2.has(ctx.container)) {
|
|
364
364
|
throw new Error(
|
|
@@ -370,7 +370,7 @@ async function transcode(source, options = {}) {
|
|
|
370
370
|
async function attemptTranscode(ctx, outputFormat, videoCodec, audioCodec, quality, options) {
|
|
371
371
|
const mb = await import('mediabunny');
|
|
372
372
|
const input = new mb.Input({
|
|
373
|
-
source: await
|
|
373
|
+
source: await chunkO34444ID_cjs.buildMediabunnySourceFromInput(mb, ctx.source),
|
|
374
374
|
formats: mb.ALL_FORMATS
|
|
375
375
|
});
|
|
376
376
|
const target = new mb.BufferTarget();
|
|
@@ -552,23 +552,23 @@ function qualityToMediabunny(mb, quality) {
|
|
|
552
552
|
|
|
553
553
|
Object.defineProperty(exports, "UnifiedPlayer", {
|
|
554
554
|
enumerable: true,
|
|
555
|
-
get: function () { return
|
|
555
|
+
get: function () { return chunkO34444ID_cjs.UnifiedPlayer; }
|
|
556
556
|
});
|
|
557
557
|
Object.defineProperty(exports, "classify", {
|
|
558
558
|
enumerable: true,
|
|
559
|
-
get: function () { return
|
|
559
|
+
get: function () { return chunkO34444ID_cjs.classifyContext; }
|
|
560
560
|
});
|
|
561
561
|
Object.defineProperty(exports, "createPlayer", {
|
|
562
562
|
enumerable: true,
|
|
563
|
-
get: function () { return
|
|
563
|
+
get: function () { return chunkO34444ID_cjs.createPlayer; }
|
|
564
564
|
});
|
|
565
565
|
Object.defineProperty(exports, "probe", {
|
|
566
566
|
enumerable: true,
|
|
567
|
-
get: function () { return
|
|
567
|
+
get: function () { return chunkO34444ID_cjs.probe; }
|
|
568
568
|
});
|
|
569
569
|
Object.defineProperty(exports, "srtToVtt", {
|
|
570
570
|
enumerable: true,
|
|
571
|
-
get: function () { return
|
|
571
|
+
get: function () { return chunkO34444ID_cjs.srtToVtt; }
|
|
572
572
|
});
|
|
573
573
|
exports.remux = remux;
|
|
574
574
|
exports.transcode = transcode;
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { probe, avbridgeVideoToMediabunny, avbridgeAudioToMediabunny, buildMediabunnySourceFromInput } from './chunk-
|
|
2
|
-
export { UnifiedPlayer, classifyContext as classify, createPlayer, probe, srtToVtt } from './chunk-
|
|
1
|
+
import { probe, avbridgeVideoToMediabunny, avbridgeAudioToMediabunny, buildMediabunnySourceFromInput } from './chunk-CUQD23WO.js';
|
|
2
|
+
export { UnifiedPlayer, classifyContext as classify, createPlayer, probe, srtToVtt } from './chunk-CUQD23WO.js';
|
|
3
3
|
import { normalizeSource } from './chunk-PQTZS7OA.js';
|
|
4
4
|
import { prepareLibavInput } from './chunk-WD2ZNQA7.js';
|
|
5
|
-
import './chunk-
|
|
5
|
+
import './chunk-EJH67FXG.js';
|
|
6
6
|
import './chunk-J5MCMN3S.js';
|
|
7
7
|
|
|
8
8
|
// src/convert/remux.ts
|
|
@@ -116,7 +116,7 @@ async function remuxViaLibav(ctx, outputFormat, options) {
|
|
|
116
116
|
let loadLibav;
|
|
117
117
|
let pickLibavVariant;
|
|
118
118
|
try {
|
|
119
|
-
const loader = await import('./libav-loader-
|
|
119
|
+
const loader = await import('./libav-loader-XKH2TKUW.js');
|
|
120
120
|
const routing = await import('./variant-routing-JOBWXYKD.js');
|
|
121
121
|
loadLibav = loader.loadLibav;
|
|
122
122
|
pickLibavVariant = routing.pickLibavVariant;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkJQH6D4OE_cjs = require('./chunk-JQH6D4OE.cjs');
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Object.defineProperty(exports, "loadLibav", {
|
|
8
|
+
enumerable: true,
|
|
9
|
+
get: function () { return chunkJQH6D4OE_cjs.loadLibav; }
|
|
10
|
+
});
|
|
11
|
+
//# sourceMappingURL=libav-loader-6APXVNIV.cjs.map
|
|
12
|
+
//# sourceMappingURL=libav-loader-6APXVNIV.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"libav-loader-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"libav-loader-6APXVNIV.cjs"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"libav-loader-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"libav-loader-XKH2TKUW.js"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "avbridge",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Play and convert arbitrary video files in the browser. Native, remux, hybrid, fallback, and transcode — one API.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Keishi Hattori",
|
|
@@ -34,7 +34,8 @@
|
|
|
34
34
|
"type": "module",
|
|
35
35
|
"sideEffects": [
|
|
36
36
|
"./dist/element.js",
|
|
37
|
-
"./dist/element.cjs"
|
|
37
|
+
"./dist/element.cjs",
|
|
38
|
+
"./dist/element-browser.js"
|
|
38
39
|
],
|
|
39
40
|
"main": "./dist/index.cjs",
|
|
40
41
|
"module": "./dist/index.js",
|
|
@@ -49,16 +50,27 @@
|
|
|
49
50
|
"types": "./dist/element.d.ts",
|
|
50
51
|
"import": "./dist/element.js",
|
|
51
52
|
"require": "./dist/element.cjs"
|
|
52
|
-
}
|
|
53
|
+
},
|
|
54
|
+
"./element-browser": {
|
|
55
|
+
"types": "./dist/element.d.ts",
|
|
56
|
+
"import": "./dist/element-browser.js"
|
|
57
|
+
},
|
|
58
|
+
"./vendor/*": "./vendor/*"
|
|
53
59
|
},
|
|
54
60
|
"files": [
|
|
55
61
|
"dist",
|
|
56
62
|
"src",
|
|
63
|
+
"vendor/libav/avbridge",
|
|
64
|
+
"vendor/libav/webcodecs",
|
|
65
|
+
"vendor/libav/README.md",
|
|
57
66
|
"README.md",
|
|
58
67
|
"CHANGELOG.md",
|
|
59
|
-
"LICENSE"
|
|
68
|
+
"LICENSE",
|
|
69
|
+
"NOTICE.md",
|
|
70
|
+
"THIRD_PARTY_LICENSES.md"
|
|
60
71
|
],
|
|
61
72
|
"scripts": {
|
|
73
|
+
"prebuild": "node scripts/copy-libav.mjs",
|
|
62
74
|
"build": "tsup",
|
|
63
75
|
"typecheck": "tsc --noEmit",
|
|
64
76
|
"test": "vitest run",
|
|
@@ -76,12 +88,12 @@
|
|
|
76
88
|
"audit:bundle": "node scripts/bundle-audit.mjs"
|
|
77
89
|
},
|
|
78
90
|
"dependencies": {
|
|
91
|
+
"@libav.js/variant-webcodecs": "^6.8.8",
|
|
92
|
+
"libavjs-webcodecs-bridge": "^0.3.2",
|
|
79
93
|
"mediabunny": "^1.40.1"
|
|
80
94
|
},
|
|
81
95
|
"optionalDependencies": {
|
|
82
|
-
"@libav.js/types": "^6.8.8"
|
|
83
|
-
"@libav.js/variant-webcodecs": "^6.8.8",
|
|
84
|
-
"libavjs-webcodecs-bridge": "^0.3.2"
|
|
96
|
+
"@libav.js/types": "^6.8.8"
|
|
85
97
|
},
|
|
86
98
|
"devDependencies": {
|
|
87
99
|
"@types/node": "^20.11.0",
|
|
@@ -92,6 +92,31 @@ async function loadVariant(
|
|
|
92
92
|
// the same convention (`libav-webcodecs.mjs`, `libav-default.mjs`).
|
|
93
93
|
const variantUrl = `${base}/libav-${variant}.mjs`;
|
|
94
94
|
|
|
95
|
+
// Preflight HEAD-ish check: issue a bytes=0-0 range request so a missing
|
|
96
|
+
// file fails fast with a clear error instead of hanging deep inside the
|
|
97
|
+
// dynamic import or inside libav's own WASM instantiation. Surfaces the
|
|
98
|
+
// most common mistake ("libav base path is wrong") in <100 ms instead of
|
|
99
|
+
// an indeterminate stall.
|
|
100
|
+
if (typeof fetch === "function") {
|
|
101
|
+
try {
|
|
102
|
+
const head = await fetch(variantUrl, { method: "GET", headers: { Range: "bytes=0-0" } });
|
|
103
|
+
if (!head.ok && head.status !== 206) {
|
|
104
|
+
throw new Error(
|
|
105
|
+
`HTTP ${head.status} ${head.statusText} — check that libav files are served ` +
|
|
106
|
+
`at ${base}/ (override via globalThis.AVBRIDGE_LIBAV_BASE)`,
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
// Drain the tiny response so the connection can be reused.
|
|
110
|
+
try { await head.arrayBuffer(); } catch { /* ignore */ }
|
|
111
|
+
} catch (err) {
|
|
112
|
+
cache.delete(key);
|
|
113
|
+
throw chain(
|
|
114
|
+
`libav.js "${variant}" variant not reachable at ${variantUrl}`,
|
|
115
|
+
err,
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
95
120
|
let mod: LoadedVariant;
|
|
96
121
|
try {
|
|
97
122
|
// @ts-ignore runtime URL
|
|
@@ -105,10 +130,9 @@ async function loadVariant(
|
|
|
105
130
|
const hint =
|
|
106
131
|
variant === "avbridge"
|
|
107
132
|
? `The "avbridge" variant is a custom local build. Run \`./scripts/build-libav.sh\` ` +
|
|
108
|
-
`to produce it (requires Emscripten; ~15-30 min the first time)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
`node_modules/@libav.js/variant-${variant}/dist/* into the URL space).`;
|
|
133
|
+
`to produce it (requires Emscripten; ~15-30 min the first time).`
|
|
134
|
+
: `Make sure the variant files are present at ${base}/ (set ` +
|
|
135
|
+
`globalThis.AVBRIDGE_LIBAV_BASE to override the default lookup path).`;
|
|
112
136
|
throw new Error(
|
|
113
137
|
`failed to load libav.js "${variant}" variant from ${variantUrl}. ${hint} ` +
|
|
114
138
|
`Original error: ${(err as Error).message || String(err)}`,
|
|
@@ -161,15 +185,33 @@ function buildOpts(base: string, wantThreads: boolean): Record<string, unknown>
|
|
|
161
185
|
}
|
|
162
186
|
|
|
163
187
|
function libavBaseUrl(): string {
|
|
188
|
+
// Consumer override — the documented "LGPL replaceability" hook.
|
|
189
|
+
// Setting `globalThis.AVBRIDGE_LIBAV_BASE = "/my/path"` lets anyone swap
|
|
190
|
+
// in a different libav build (custom fragments, security patches, etc.)
|
|
191
|
+
// without rebuilding avbridge.
|
|
164
192
|
const override =
|
|
165
193
|
typeof globalThis !== "undefined"
|
|
166
194
|
? (globalThis as { AVBRIDGE_LIBAV_BASE?: string }).AVBRIDGE_LIBAV_BASE
|
|
167
195
|
: undefined;
|
|
168
196
|
if (override) return override;
|
|
169
|
-
|
|
170
|
-
|
|
197
|
+
|
|
198
|
+
// Default: resolve relative to this module's URL. When avbridge is installed
|
|
199
|
+
// under `node_modules/avbridge/`, this module lives at `dist/chunk-*.js` (or
|
|
200
|
+
// `dist/element-browser.js` for the browser entry) and `../vendor/libav`
|
|
201
|
+
// resolves to `node_modules/avbridge/vendor/libav`, where the build step
|
|
202
|
+
// vendored every variant's binaries. That's the zero-config path.
|
|
203
|
+
//
|
|
204
|
+
// `import.meta.url` throws in some synthetic environments (CJS tests, some
|
|
205
|
+
// SSR evaluators). If it fails, fall back to the legacy `/libav` path so
|
|
206
|
+
// consumers who relied on the pre-2.1 behavior still work.
|
|
207
|
+
try {
|
|
208
|
+
return new URL("../vendor/libav", import.meta.url).href;
|
|
209
|
+
} catch {
|
|
210
|
+
if (typeof location !== "undefined" && location.protocol.startsWith("http")) {
|
|
211
|
+
return `${location.origin}/libav`;
|
|
212
|
+
}
|
|
213
|
+
return "/libav";
|
|
171
214
|
}
|
|
172
|
-
return "/libav";
|
|
173
215
|
}
|
|
174
216
|
|
|
175
217
|
function chain(message: string, err: unknown): Error {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser stub for `node:fs/promises`.
|
|
3
|
+
*
|
|
4
|
+
* mediabunny's ESM entry transitively imports `node:fs/promises` from its
|
|
5
|
+
* Node-compat `node.js` file (used by `FilePathSource` / `FilePathTarget`).
|
|
6
|
+
* Its `package.json` has a `browser` field that tells bundlers to stub this
|
|
7
|
+
* out at build time — but import maps in the browser can't read
|
|
8
|
+
* `package.json`, so for the pre-bundled `dist/element-browser.js` entry we
|
|
9
|
+
* have tsup/esbuild alias `node:fs/promises` → this file at bundle time.
|
|
10
|
+
*
|
|
11
|
+
* Any caller that reaches this code is trying to use a Node-only feature in
|
|
12
|
+
* the browser and will fail loudly with a clear message instead of hanging
|
|
13
|
+
* or throwing an opaque `undefined is not a function` somewhere deep in
|
|
14
|
+
* mediabunny.
|
|
15
|
+
*/
|
|
16
|
+
function notAvailable(): never {
|
|
17
|
+
throw new Error(
|
|
18
|
+
"node:fs/promises is not available in the browser. " +
|
|
19
|
+
"The file-path APIs (FilePathSource / FilePathTarget) only work in Node.js.",
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const open = notAvailable;
|
|
24
|
+
export const readFile = notAvailable;
|
|
25
|
+
export const writeFile = notAvailable;
|
|
26
|
+
export const stat = notAvailable;
|
|
27
|
+
export const mkdir = notAvailable;
|
|
28
|
+
export const rm = notAvailable;
|
|
29
|
+
export const unlink = notAvailable;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# vendor/libav — avbridge custom libav.js build
|
|
2
|
+
|
|
3
|
+
This directory holds a **custom-built libav.js variant** with the demuxers
|
|
4
|
+
and decoders avbridge needs for legacy file playback. The npm-published variants
|
|
5
|
+
of libav.js are intentionally minimal — none of them include the AVI/ASF/FLV
|
|
6
|
+
demuxers or the legacy codec decoders (WMV3, MPEG-4 Part 2, MS-MPEG4, VC-1,
|
|
7
|
+
…). The supported way to get those is to build a custom variant locally.
|
|
8
|
+
|
|
9
|
+
## TL;DR
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
./scripts/build-libav.sh # 15-30 minutes the first time
|
|
13
|
+
npm run predemo # copies vendor/libav/* into demo/public/libav/avbridge/
|
|
14
|
+
npm run demo
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
After the first run, `~/.cache/avbridge/` contains the emsdk and libav.js source
|
|
18
|
+
trees so subsequent rebuilds (e.g. after editing the fragment list) are
|
|
19
|
+
incremental and much faster.
|
|
20
|
+
|
|
21
|
+
## What gets built
|
|
22
|
+
|
|
23
|
+
The fragment list in `scripts/build-libav.sh` enables:
|
|
24
|
+
|
|
25
|
+
| Demuxers | Decoders (video) | Decoders (audio) | Bitstream filters |
|
|
26
|
+
|---|---|---|---|
|
|
27
|
+
| avi, asf, flv, matroska, mov, mp3, ogg, wav, aac | h264, hevc, mpeg4 (Part 2 / DivX / Xvid), msmpeg4 v1/v2/v3, wmv1/2/3, vc1, mpeg1, mpeg2 | aac, mp3, ac3, eac3, wmav1/v2, wmapro | mpeg4_unpack_bframes |
|
|
28
|
+
|
|
29
|
+
Plus the parsers needed by each codec, plus `swscale` (video colorspace
|
|
30
|
+
conversion) and `swresample` (audio resampling).
|
|
31
|
+
|
|
32
|
+
The `mpeg4_unpack_bframes` BSF fixes the "packed B-frames" oddity in some
|
|
33
|
+
DivX files where two frames are stored in one packet — without the BSF the
|
|
34
|
+
decoder produces frames with fuzzy timing that the renderer drops as late.
|
|
35
|
+
|
|
36
|
+
Output binary is roughly 10–15 MB. It is loaded **lazily** by avbridge — only
|
|
37
|
+
when probe or classification routes a file to it. Users who only ever play
|
|
38
|
+
modern MP4/MKV/WebM never download it.
|
|
39
|
+
|
|
40
|
+
## Compile flags
|
|
41
|
+
|
|
42
|
+
The default libav.js Makefile uses `OPTFLAGS=-Oz` (size-optimized, slow).
|
|
43
|
+
We override to `-O3 -msimd128`:
|
|
44
|
+
|
|
45
|
+
- `-O3` — full optimization, ~1.5–2× speedup over `-Oz` for video decode.
|
|
46
|
+
- `-msimd128` — emit WebAssembly SIMD instructions. emscripten translates
|
|
47
|
+
ffmpeg's SSE2-style intrinsics into WASM SIMD ops, which gives another
|
|
48
|
+
~1.5–2× for IDCT, motion compensation, deblocking. Requires a browser
|
|
49
|
+
with WASM SIMD (Chrome 91+, Firefox 89+, Safari 16.4+).
|
|
50
|
+
|
|
51
|
+
Override at invocation time:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
avbridge_LIBAV_OPTFLAGS="-O2" ./scripts/build-libav.sh
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The script tracks the hash of `(fragments, OPTFLAGS)` in
|
|
58
|
+
`~/.cache/avbridge/libav.js/.avbridge-build-inputs`. If you re-run with different
|
|
59
|
+
inputs, it wipes `build/ffmpeg-*` and `build/inst` to force a clean
|
|
60
|
+
rebuild — Make can't detect OPTFLAGS changes on its own, so without the
|
|
61
|
+
hash check stale `-Oz` objects would silently survive.
|
|
62
|
+
|
|
63
|
+
## Adding or removing codecs
|
|
64
|
+
|
|
65
|
+
Edit the `VARIANT_FRAGMENTS` heredoc in `scripts/build-libav.sh`. The
|
|
66
|
+
fragment names follow FFmpeg's `--enable-decoder=<name>` /
|
|
67
|
+
`--enable-demuxer=<name>` conventions:
|
|
68
|
+
|
|
69
|
+
- `demuxer-<format>` — read this container
|
|
70
|
+
- `decoder-<codec>` — decode this codec
|
|
71
|
+
- `parser-<codec>` — parse stream metadata for this codec (tiny, usually
|
|
72
|
+
needed alongside the matching decoder for seeking)
|
|
73
|
+
|
|
74
|
+
See [`docs/CONFIG.md`][config-md] in the libav.js repo for the full grammar
|
|
75
|
+
and [`configs/mkconfigs.js`][mkconfigs] for the names of every fragment.
|
|
76
|
+
|
|
77
|
+
[config-md]: https://github.com/Yahweasel/libav.js/blob/master/docs/CONFIG.md
|
|
78
|
+
[mkconfigs]: https://github.com/Yahweasel/libav.js/blob/master/configs/mkconfigs.js
|
|
79
|
+
|
|
80
|
+
After editing, re-run `./scripts/build-libav.sh` — the make-based build is
|
|
81
|
+
incremental, so changes only rebuild the affected pieces.
|
|
82
|
+
|
|
83
|
+
## Build script behavior
|
|
84
|
+
|
|
85
|
+
- Caches everything under `$avbridge_BUILD_CACHE` (default `~/.cache/avbridge`).
|
|
86
|
+
Override the cache location by setting that env var.
|
|
87
|
+
- Installs **emsdk** into `~/.cache/avbridge/emsdk` — does **not** touch your
|
|
88
|
+
system Python, Homebrew, or any global package manager. Only the
|
|
89
|
+
`emsdk/` directory is modified.
|
|
90
|
+
- Clones **libav.js** into `~/.cache/avbridge/libav.js` and checks out the
|
|
91
|
+
pinned `v6.8.8.0` tag.
|
|
92
|
+
- Writes a custom variant config via `node configs/mkconfig.js avbridge [...]`.
|
|
93
|
+
- Runs `make build-avbridge`, which downloads ffmpeg sources, applies libav.js's
|
|
94
|
+
patches, and compiles to WASM.
|
|
95
|
+
- Copies the resulting `libav-6.8.8.0-avbridge.{mjs,wasm.mjs,wasm.wasm,…}` into
|
|
96
|
+
this directory.
|
|
97
|
+
|
|
98
|
+
To force a clean rebuild from scratch:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
avbridge_LIBAV_CLEAN=1 ./scripts/build-libav.sh
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Loader integration
|
|
105
|
+
|
|
106
|
+
The avbridge runtime knows about three variants — `webcodecs`, `default`, `avbridge`
|
|
107
|
+
— defined in `src/strategies/fallback/libav-loader.ts`. The variant routing
|
|
108
|
+
in `src/strategies/fallback/variant-routing.ts` decides which one a given
|
|
109
|
+
`MediaContext` needs:
|
|
110
|
+
|
|
111
|
+
- Modern containers + browser-supported codecs → `webcodecs`
|
|
112
|
+
- AVI / ASF / FLV containers, or any of the legacy codec set → `avbridge`
|
|
113
|
+
|
|
114
|
+
The loader fetches each variant via a runtime URL with `/* @vite-ignore */`,
|
|
115
|
+
so Vite never pre-bundles it. The variant's `import.meta.url` resolves to
|
|
116
|
+
`/libav/<variant>/libav-<variant>.mjs` and its sibling `.wasm.mjs` /
|
|
117
|
+
`.wasm.wasm` files are served from the same directory.
|
|
118
|
+
|
|
119
|
+
## Licensing
|
|
120
|
+
|
|
121
|
+
libav.js is **LGPL-2.1**. If you ship a custom variant in a product, you
|
|
122
|
+
must also distribute the corresponding source / build script. Keep
|
|
123
|
+
`scripts/build-libav.sh` and the libav.js repo URL alongside any binaries
|
|
124
|
+
you redistribute.
|
|
125
|
+
|
|
126
|
+
## Files in this directory after a successful build
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
vendor/libav/
|
|
130
|
+
├── README.md ← this file
|
|
131
|
+
├── libav-avbridge.mjs ← entry point (loaded by avbridge)
|
|
132
|
+
├── libav-6.8.8.0-avbridge.wasm.mjs ← WASM build factory
|
|
133
|
+
├── libav-6.8.8.0-avbridge.wasm.wasm ← compiled binary
|
|
134
|
+
├── libav-6.8.8.0-avbridge.thr.mjs ← threaded build factory (optional)
|
|
135
|
+
├── libav-6.8.8.0-avbridge.thr.wasm ← threaded binary (optional)
|
|
136
|
+
└── libav-6.8.8.0-avbridge.asm.{js,mjs} ← asm.js fallback (very rarely used)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
The script also copies `.dbg.*` debug builds when present.
|