avbridge 2.1.1 → 2.2.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 +136 -0
- package/dist/{avi-GNTV5ZOH.cjs → avi-6SJLWIWW.cjs} +19 -4
- package/dist/avi-6SJLWIWW.cjs.map +1 -0
- package/dist/{avi-V6HYQVR2.js → avi-GCGM7OJI.js} +18 -3
- package/dist/avi-GCGM7OJI.js.map +1 -0
- package/dist/{chunk-EJH67FXG.js → chunk-5DMTJVIU.js} +99 -3
- package/dist/chunk-5DMTJVIU.js.map +1 -0
- package/dist/{chunk-CUQD23WO.js → chunk-C5VA5U5O.js} +122 -23
- package/dist/chunk-C5VA5U5O.js.map +1 -0
- package/dist/{chunk-JQH6D4OE.cjs → chunk-G4APZMCP.cjs} +100 -3
- package/dist/chunk-G4APZMCP.cjs.map +1 -0
- package/dist/{chunk-Y5FYF5KG.cjs → chunk-HZLQNKFN.cjs} +5 -2
- package/dist/chunk-HZLQNKFN.cjs.map +1 -0
- package/dist/{chunk-PQTZS7OA.js → chunk-ILKDNBSE.js} +5 -2
- package/dist/chunk-ILKDNBSE.js.map +1 -0
- package/dist/{chunk-O34444ID.cjs → chunk-OE66B34H.cjs} +126 -27
- package/dist/chunk-OE66B34H.cjs.map +1 -0
- package/dist/element-browser.js +244 -19
- package/dist/element-browser.js.map +1 -1
- package/dist/element.cjs +9 -5
- package/dist/element.cjs.map +1 -1
- package/dist/element.d.cts +1 -1
- package/dist/element.d.ts +1 -1
- package/dist/element.js +8 -4
- package/dist/element.js.map +1 -1
- package/dist/index.cjs +18 -18
- package/dist/index.d.cts +12 -8
- package/dist/index.d.ts +12 -8
- package/dist/index.js +5 -5
- package/dist/libav-loader-27RDIN2I.js +3 -0
- package/dist/{libav-loader-XKH2TKUW.js.map → libav-loader-27RDIN2I.js.map} +1 -1
- package/dist/libav-loader-IV4AJ2HW.cjs +12 -0
- package/dist/{libav-loader-6APXVNIV.cjs.map → libav-loader-IV4AJ2HW.cjs.map} +1 -1
- package/dist/{player-BdtUG4rh.d.cts → player-DUyvltvy.d.cts} +3 -3
- package/dist/{player-BdtUG4rh.d.ts → player-DUyvltvy.d.ts} +3 -3
- package/dist/source-CN43EI7Z.cjs +28 -0
- package/dist/{source-SC6ZEQYR.cjs.map → source-CN43EI7Z.cjs.map} +1 -1
- package/dist/source-FFZ7TW2B.js +3 -0
- package/dist/{source-ZFS4H7J3.js.map → source-FFZ7TW2B.js.map} +1 -1
- package/package.json +1 -1
- package/src/classify/rules.ts +9 -2
- package/src/element/avbridge-video.ts +12 -1
- package/src/player.ts +22 -1
- package/src/probe/avi.ts +8 -1
- package/src/probe/index.ts +30 -9
- package/src/strategies/fallback/audio-output.ts +25 -3
- package/src/strategies/fallback/decoder.ts +96 -8
- package/src/strategies/fallback/index.ts +90 -6
- package/src/strategies/fallback/libav-loader.ts +12 -0
- package/src/strategies/fallback/video-renderer.ts +29 -4
- package/src/strategies/remux/pipeline.ts +10 -1
- package/src/types.ts +10 -1
- package/src/util/debug.ts +131 -0
- package/src/util/source.ts +4 -0
- package/vendor/libav/avbridge/libav-6.8.8.0-avbridge.wasm.mjs +1 -1
- package/vendor/libav/avbridge/libav-6.8.8.0-avbridge.wasm.wasm +0 -0
- package/dist/avi-GNTV5ZOH.cjs.map +0 -1
- package/dist/avi-V6HYQVR2.js.map +0 -1
- package/dist/chunk-CUQD23WO.js.map +0 -1
- package/dist/chunk-EJH67FXG.js.map +0 -1
- package/dist/chunk-JQH6D4OE.cjs.map +0 -1
- package/dist/chunk-O34444ID.cjs.map +0 -1
- package/dist/chunk-PQTZS7OA.js.map +0 -1
- package/dist/chunk-Y5FYF5KG.cjs.map +0 -1
- package/dist/libav-loader-6APXVNIV.cjs +0 -12
- package/dist/libav-loader-XKH2TKUW.js +0 -3
- package/dist/source-SC6ZEQYR.cjs +0 -28
- package/dist/source-ZFS4H7J3.js +0 -3
package/dist/index.cjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
3
|
+
var chunkOE66B34H_cjs = require('./chunk-OE66B34H.cjs');
|
|
4
|
+
var chunkHZLQNKFN_cjs = require('./chunk-HZLQNKFN.cjs');
|
|
5
5
|
var chunkL4NPOJ36_cjs = require('./chunk-L4NPOJ36.cjs');
|
|
6
|
-
require('./chunk-
|
|
6
|
+
require('./chunk-G4APZMCP.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 chunkOE66B34H_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 = chunkOE66B34H_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 = chunkOE66B34H_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 chunkOE66B34H_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-IV4AJ2HW.cjs');
|
|
121
121
|
const routing = await import('./variant-routing-GOHB2RZN.cjs');
|
|
122
122
|
loadLibav = loader.loadLibav;
|
|
123
123
|
pickLibavVariant = routing.pickLibavVariant;
|
|
@@ -128,7 +128,7 @@ async function remuxViaLibav(ctx, outputFormat, options) {
|
|
|
128
128
|
}
|
|
129
129
|
const variant = pickLibavVariant(ctx);
|
|
130
130
|
const libav = await loadLibav(variant);
|
|
131
|
-
const normalized = await
|
|
131
|
+
const normalized = await chunkHZLQNKFN_cjs.normalizeSource(ctx.source);
|
|
132
132
|
const filename = ctx.name ?? `remux-input-${Date.now()}`;
|
|
133
133
|
const handle = await chunkL4NPOJ36_cjs.prepareLibavInput(libav, filename, normalized);
|
|
134
134
|
try {
|
|
@@ -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 ? chunkOE66B34H_cjs.avbridgeVideoToMediabunny(videoTrackInfo.codec) : null;
|
|
150
|
+
const mbAudioCodec = audioTrackInfo ? chunkOE66B34H_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 chunkOE66B34H_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 chunkOE66B34H_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 chunkOE66B34H_cjs.UnifiedPlayer; }
|
|
556
556
|
});
|
|
557
557
|
Object.defineProperty(exports, "classify", {
|
|
558
558
|
enumerable: true,
|
|
559
|
-
get: function () { return
|
|
559
|
+
get: function () { return chunkOE66B34H_cjs.classifyContext; }
|
|
560
560
|
});
|
|
561
561
|
Object.defineProperty(exports, "createPlayer", {
|
|
562
562
|
enumerable: true,
|
|
563
|
-
get: function () { return
|
|
563
|
+
get: function () { return chunkOE66B34H_cjs.createPlayer; }
|
|
564
564
|
});
|
|
565
565
|
Object.defineProperty(exports, "probe", {
|
|
566
566
|
enumerable: true,
|
|
567
|
-
get: function () { return
|
|
567
|
+
get: function () { return chunkOE66B34H_cjs.probe; }
|
|
568
568
|
});
|
|
569
569
|
Object.defineProperty(exports, "srtToVtt", {
|
|
570
570
|
enumerable: true,
|
|
571
|
-
get: function () { return
|
|
571
|
+
get: function () { return chunkOE66B34H_cjs.srtToVtt; }
|
|
572
572
|
});
|
|
573
573
|
exports.remux = remux;
|
|
574
574
|
exports.transcode = transcode;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { M as MediaContext, C as Classification, a as MediaInput, b as ConvertOptions, c as ConvertResult, T as TranscodeOptions } from './player-
|
|
2
|
-
export { A as AudioCodec, d as AudioTrackInfo, e as ContainerKind, f as CreatePlayerOptions, D as DiagnosticsSnapshot, H as HardwareAccelerationHint, O as OutputAudioCodec, g as OutputFormat, h as OutputVideoCodec, P as PlaybackSession, i as PlayerEventMap, j as PlayerEventName, k as Plugin, l as ProgressInfo, S as StrategyClass, m as StrategyName, n as SubtitleTrackInfo, o as TranscodeQuality, U as UnifiedPlayer, V as VideoCodec, p as VideoTrackInfo, q as createPlayer } from './player-
|
|
1
|
+
import { M as MediaContext, C as Classification, a as MediaInput, b as ConvertOptions, c as ConvertResult, T as TranscodeOptions } from './player-DUyvltvy.cjs';
|
|
2
|
+
export { A as AudioCodec, d as AudioTrackInfo, e as ContainerKind, f as CreatePlayerOptions, D as DiagnosticsSnapshot, H as HardwareAccelerationHint, O as OutputAudioCodec, g as OutputFormat, h as OutputVideoCodec, P as PlaybackSession, i as PlayerEventMap, j as PlayerEventName, k as Plugin, l as ProgressInfo, S as StrategyClass, m as StrategyName, n as SubtitleTrackInfo, o as TranscodeQuality, U as UnifiedPlayer, V as VideoCodec, p as VideoTrackInfo, q as createPlayer } from './player-DUyvltvy.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Pure classification — no I/O, no async. Test-friendly.
|
|
@@ -11,12 +11,16 @@ declare function classifyContext(ctx: MediaContext): Classification;
|
|
|
11
11
|
*
|
|
12
12
|
* Routing:
|
|
13
13
|
* 1. Sniff the magic header. Cheap, deterministic, ignores file extensions.
|
|
14
|
-
* 2. If the container is one mediabunny supports → mediabunny
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
14
|
+
* 2. If the container is one mediabunny supports → try mediabunny first
|
|
15
|
+
* (fast path — it's a single pass of WASM-free JS parsing). If mediabunny
|
|
16
|
+
* throws (e.g. an assertion on an unsupported sample entry like `mp4v`
|
|
17
|
+
* for MPEG-4 Part 2 in ISOBMFF, or an exotic MKV codec), fall through to
|
|
18
|
+
* libav.js which handles the long tail of codecs mediabunny doesn't.
|
|
19
|
+
* The combined-error case surfaces *both* failures so the user sees
|
|
20
|
+
* which path each step took.
|
|
21
|
+
* 3. If sniffing identifies AVI/ASF/FLV (or `unknown`) → libav.js directly.
|
|
22
|
+
* mediabunny can't read those containers at all, so there's no fast path
|
|
23
|
+
* to try.
|
|
20
24
|
*/
|
|
21
25
|
declare function probe(source: MediaInput): Promise<MediaContext>;
|
|
22
26
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { M as MediaContext, C as Classification, a as MediaInput, b as ConvertOptions, c as ConvertResult, T as TranscodeOptions } from './player-
|
|
2
|
-
export { A as AudioCodec, d as AudioTrackInfo, e as ContainerKind, f as CreatePlayerOptions, D as DiagnosticsSnapshot, H as HardwareAccelerationHint, O as OutputAudioCodec, g as OutputFormat, h as OutputVideoCodec, P as PlaybackSession, i as PlayerEventMap, j as PlayerEventName, k as Plugin, l as ProgressInfo, S as StrategyClass, m as StrategyName, n as SubtitleTrackInfo, o as TranscodeQuality, U as UnifiedPlayer, V as VideoCodec, p as VideoTrackInfo, q as createPlayer } from './player-
|
|
1
|
+
import { M as MediaContext, C as Classification, a as MediaInput, b as ConvertOptions, c as ConvertResult, T as TranscodeOptions } from './player-DUyvltvy.js';
|
|
2
|
+
export { A as AudioCodec, d as AudioTrackInfo, e as ContainerKind, f as CreatePlayerOptions, D as DiagnosticsSnapshot, H as HardwareAccelerationHint, O as OutputAudioCodec, g as OutputFormat, h as OutputVideoCodec, P as PlaybackSession, i as PlayerEventMap, j as PlayerEventName, k as Plugin, l as ProgressInfo, S as StrategyClass, m as StrategyName, n as SubtitleTrackInfo, o as TranscodeQuality, U as UnifiedPlayer, V as VideoCodec, p as VideoTrackInfo, q as createPlayer } from './player-DUyvltvy.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Pure classification — no I/O, no async. Test-friendly.
|
|
@@ -11,12 +11,16 @@ declare function classifyContext(ctx: MediaContext): Classification;
|
|
|
11
11
|
*
|
|
12
12
|
* Routing:
|
|
13
13
|
* 1. Sniff the magic header. Cheap, deterministic, ignores file extensions.
|
|
14
|
-
* 2. If the container is one mediabunny supports → mediabunny
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
14
|
+
* 2. If the container is one mediabunny supports → try mediabunny first
|
|
15
|
+
* (fast path — it's a single pass of WASM-free JS parsing). If mediabunny
|
|
16
|
+
* throws (e.g. an assertion on an unsupported sample entry like `mp4v`
|
|
17
|
+
* for MPEG-4 Part 2 in ISOBMFF, or an exotic MKV codec), fall through to
|
|
18
|
+
* libav.js which handles the long tail of codecs mediabunny doesn't.
|
|
19
|
+
* The combined-error case surfaces *both* failures so the user sees
|
|
20
|
+
* which path each step took.
|
|
21
|
+
* 3. If sniffing identifies AVI/ASF/FLV (or `unknown`) → libav.js directly.
|
|
22
|
+
* mediabunny can't read those containers at all, so there's no fast path
|
|
23
|
+
* to try.
|
|
20
24
|
*/
|
|
21
25
|
declare function probe(source: MediaInput): Promise<MediaContext>;
|
|
22
26
|
|
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-
|
|
3
|
-
import { normalizeSource } from './chunk-
|
|
1
|
+
import { probe, avbridgeVideoToMediabunny, avbridgeAudioToMediabunny, buildMediabunnySourceFromInput } from './chunk-C5VA5U5O.js';
|
|
2
|
+
export { UnifiedPlayer, classifyContext as classify, createPlayer, probe, srtToVtt } from './chunk-C5VA5U5O.js';
|
|
3
|
+
import { normalizeSource } from './chunk-ILKDNBSE.js';
|
|
4
4
|
import { prepareLibavInput } from './chunk-WD2ZNQA7.js';
|
|
5
|
-
import './chunk-
|
|
5
|
+
import './chunk-5DMTJVIU.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-27RDIN2I.js');
|
|
120
120
|
const routing = await import('./variant-routing-JOBWXYKD.js');
|
|
121
121
|
loadLibav = loader.loadLibav;
|
|
122
122
|
pickLibavVariant = routing.pickLibavVariant;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"libav-loader-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"libav-loader-27RDIN2I.js"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkG4APZMCP_cjs = require('./chunk-G4APZMCP.cjs');
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Object.defineProperty(exports, "loadLibav", {
|
|
8
|
+
enumerable: true,
|
|
9
|
+
get: function () { return chunkG4APZMCP_cjs.loadLibav; }
|
|
10
|
+
});
|
|
11
|
+
//# sourceMappingURL=libav-loader-IV4AJ2HW.cjs.map
|
|
12
|
+
//# sourceMappingURL=libav-loader-IV4AJ2HW.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"libav-loader-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"libav-loader-IV4AJ2HW.cjs"}
|
|
@@ -13,11 +13,11 @@
|
|
|
13
13
|
*/
|
|
14
14
|
type MediaInput = File | Blob | string | URL | ArrayBuffer | Uint8Array;
|
|
15
15
|
/** Container format families we know about. */
|
|
16
|
-
type ContainerKind = "mp4" | "mov" | "mkv" | "webm" | "avi" | "asf" | "flv" | "ogg" | "wav" | "mp3" | "flac" | "adts" | "mpegts" | "unknown";
|
|
16
|
+
type ContainerKind = "mp4" | "mov" | "mkv" | "webm" | "avi" | "asf" | "flv" | "rm" | "ogg" | "wav" | "mp3" | "flac" | "adts" | "mpegts" | "unknown";
|
|
17
17
|
/** Video codec families. Strings, not enums, so plugins can extend. */
|
|
18
|
-
type VideoCodec = "h264" | "h265" | "vp8" | "vp9" | "av1" | "mpeg4" | "wmv3" | "vc1" | "rv40" | "mpeg2" | "mpeg1" | "theora" | (string & {});
|
|
18
|
+
type VideoCodec = "h264" | "h265" | "vp8" | "vp9" | "av1" | "mpeg4" | "wmv3" | "vc1" | "rv10" | "rv20" | "rv30" | "rv40" | "mpeg2" | "mpeg1" | "theora" | (string & {});
|
|
19
19
|
/** Audio codec families. */
|
|
20
|
-
type AudioCodec = "aac" | "mp3" | "opus" | "vorbis" | "flac" | "pcm" | "ac3" | "eac3" | "wmav2" | "wmapro" | "alac" | (string & {});
|
|
20
|
+
type AudioCodec = "aac" | "mp3" | "opus" | "vorbis" | "flac" | "pcm" | "ac3" | "eac3" | "wmav2" | "wmapro" | "alac" | "cook" | "ra_144" | "ra_288" | "sipr" | "atrac3" | (string & {});
|
|
21
21
|
interface VideoTrackInfo {
|
|
22
22
|
id: number;
|
|
23
23
|
codec: VideoCodec;
|
|
@@ -13,11 +13,11 @@
|
|
|
13
13
|
*/
|
|
14
14
|
type MediaInput = File | Blob | string | URL | ArrayBuffer | Uint8Array;
|
|
15
15
|
/** Container format families we know about. */
|
|
16
|
-
type ContainerKind = "mp4" | "mov" | "mkv" | "webm" | "avi" | "asf" | "flv" | "ogg" | "wav" | "mp3" | "flac" | "adts" | "mpegts" | "unknown";
|
|
16
|
+
type ContainerKind = "mp4" | "mov" | "mkv" | "webm" | "avi" | "asf" | "flv" | "rm" | "ogg" | "wav" | "mp3" | "flac" | "adts" | "mpegts" | "unknown";
|
|
17
17
|
/** Video codec families. Strings, not enums, so plugins can extend. */
|
|
18
|
-
type VideoCodec = "h264" | "h265" | "vp8" | "vp9" | "av1" | "mpeg4" | "wmv3" | "vc1" | "rv40" | "mpeg2" | "mpeg1" | "theora" | (string & {});
|
|
18
|
+
type VideoCodec = "h264" | "h265" | "vp8" | "vp9" | "av1" | "mpeg4" | "wmv3" | "vc1" | "rv10" | "rv20" | "rv30" | "rv40" | "mpeg2" | "mpeg1" | "theora" | (string & {});
|
|
19
19
|
/** Audio codec families. */
|
|
20
|
-
type AudioCodec = "aac" | "mp3" | "opus" | "vorbis" | "flac" | "pcm" | "ac3" | "eac3" | "wmav2" | "wmapro" | "alac" | (string & {});
|
|
20
|
+
type AudioCodec = "aac" | "mp3" | "opus" | "vorbis" | "flac" | "pcm" | "ac3" | "eac3" | "wmav2" | "wmapro" | "alac" | "cook" | "ra_144" | "ra_288" | "sipr" | "atrac3" | (string & {});
|
|
21
21
|
interface VideoTrackInfo {
|
|
22
22
|
id: number;
|
|
23
23
|
codec: VideoCodec;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkHZLQNKFN_cjs = require('./chunk-HZLQNKFN.cjs');
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Object.defineProperty(exports, "isInMemorySource", {
|
|
8
|
+
enumerable: true,
|
|
9
|
+
get: function () { return chunkHZLQNKFN_cjs.isInMemorySource; }
|
|
10
|
+
});
|
|
11
|
+
Object.defineProperty(exports, "normalizeSource", {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
get: function () { return chunkHZLQNKFN_cjs.normalizeSource; }
|
|
14
|
+
});
|
|
15
|
+
Object.defineProperty(exports, "sniffContainer", {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
get: function () { return chunkHZLQNKFN_cjs.sniffContainer; }
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(exports, "sniffContainerFromBytes", {
|
|
20
|
+
enumerable: true,
|
|
21
|
+
get: function () { return chunkHZLQNKFN_cjs.sniffContainerFromBytes; }
|
|
22
|
+
});
|
|
23
|
+
Object.defineProperty(exports, "sniffNormalizedSource", {
|
|
24
|
+
enumerable: true,
|
|
25
|
+
get: function () { return chunkHZLQNKFN_cjs.sniffNormalizedSource; }
|
|
26
|
+
});
|
|
27
|
+
//# sourceMappingURL=source-CN43EI7Z.cjs.map
|
|
28
|
+
//# sourceMappingURL=source-CN43EI7Z.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"source-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"source-CN43EI7Z.cjs"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"source-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"source-FFZ7TW2B.js"}
|
package/package.json
CHANGED
package/src/classify/rules.ts
CHANGED
|
@@ -25,8 +25,15 @@ const NATIVE_AUDIO_CODECS = new Set<AudioCodec>([
|
|
|
25
25
|
/**
|
|
26
26
|
* Codecs no major browser plays, period. These force the WASM fallback.
|
|
27
27
|
*/
|
|
28
|
-
const FALLBACK_VIDEO_CODECS = new Set<VideoCodec>([
|
|
29
|
-
|
|
28
|
+
const FALLBACK_VIDEO_CODECS = new Set<VideoCodec>([
|
|
29
|
+
"wmv3", "vc1", "mpeg4",
|
|
30
|
+
"rv10", "rv20", "rv30", "rv40",
|
|
31
|
+
"mpeg2", "mpeg1", "theora",
|
|
32
|
+
]);
|
|
33
|
+
const FALLBACK_AUDIO_CODECS = new Set<AudioCodec>([
|
|
34
|
+
"wmav2", "wmapro", "ac3", "eac3",
|
|
35
|
+
"cook", "ra_144", "ra_288", "sipr", "atrac3",
|
|
36
|
+
]);
|
|
30
37
|
|
|
31
38
|
/**
|
|
32
39
|
* Containers `<video>` plays directly. Anything else with otherwise-supported
|
|
@@ -170,11 +170,22 @@ export class AvbridgeVideoElement extends HTMLElementCtor {
|
|
|
170
170
|
constructor() {
|
|
171
171
|
super();
|
|
172
172
|
const root = this.attachShadow({ mode: "open" });
|
|
173
|
+
|
|
174
|
+
// A positioned wrapper inside the shadow root. The fallback strategy
|
|
175
|
+
// overlays a canvas on top of the <video> via `target.parentNode` —
|
|
176
|
+
// that only works if the parent is a real Element with layout. Without
|
|
177
|
+
// this wrapper, `target.parentElement` would be null (ShadowRoot is
|
|
178
|
+
// not an Element) and the canvas would never attach to the DOM.
|
|
179
|
+
const stage = document.createElement("div");
|
|
180
|
+
stage.setAttribute("part", "stage");
|
|
181
|
+
stage.style.cssText = "position:relative;width:100%;height:100%;display:block;";
|
|
182
|
+
root.appendChild(stage);
|
|
183
|
+
|
|
173
184
|
this._videoEl = document.createElement("video");
|
|
174
185
|
this._videoEl.setAttribute("part", "video");
|
|
175
186
|
this._videoEl.style.cssText = "width:100%;height:100%;display:block;background:#000;";
|
|
176
187
|
this._videoEl.playsInline = true;
|
|
177
|
-
|
|
188
|
+
stage.appendChild(this._videoEl);
|
|
178
189
|
|
|
179
190
|
// Forward the underlying <video>'s `progress` event so consumers can
|
|
180
191
|
// observe buffered-range updates without reaching into the shadow DOM.
|
package/src/player.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { Diagnostics } from "./diagnostics.js";
|
|
|
5
5
|
import { PluginRegistry } from "./plugins/registry.js";
|
|
6
6
|
import { registerBuiltins } from "./plugins/builtin.js";
|
|
7
7
|
import { discoverSidecars, attachSubtitleTracks, SubtitleResourceBag } from "./subtitles/index.js";
|
|
8
|
+
import { dbg } from "./util/debug.js";
|
|
8
9
|
import type {
|
|
9
10
|
Classification,
|
|
10
11
|
CreatePlayerOptions,
|
|
@@ -65,8 +66,14 @@ export class UnifiedPlayer {
|
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
private async bootstrap(): Promise<void> {
|
|
69
|
+
const bootstrapStart = performance.now();
|
|
68
70
|
try {
|
|
69
|
-
|
|
71
|
+
dbg.info("bootstrap", "start");
|
|
72
|
+
const ctx = await dbg.timed("probe", "probe", 3000, () => probe(this.options.source));
|
|
73
|
+
dbg.info("probe",
|
|
74
|
+
`container=${ctx.container} video=${ctx.videoTracks[0]?.codec ?? "-"} ` +
|
|
75
|
+
`audio=${ctx.audioTracks[0]?.codec ?? "-"} probedBy=${ctx.probedBy}`,
|
|
76
|
+
);
|
|
70
77
|
this.diag.recordProbe(ctx);
|
|
71
78
|
this.mediaContext = ctx;
|
|
72
79
|
|
|
@@ -99,6 +106,10 @@ export class UnifiedPlayer {
|
|
|
99
106
|
const decision = this.options.initialStrategy
|
|
100
107
|
? buildInitialDecision(this.options.initialStrategy, ctx)
|
|
101
108
|
: classify(ctx);
|
|
109
|
+
dbg.info("classify",
|
|
110
|
+
`strategy=${decision.strategy} class=${decision.class} reason="${decision.reason}"` +
|
|
111
|
+
(decision.fallbackChain ? ` fallback=${decision.fallbackChain.join("→")}` : ""),
|
|
112
|
+
);
|
|
102
113
|
this.classification = decision;
|
|
103
114
|
this.diag.recordClassification(decision);
|
|
104
115
|
|
|
@@ -137,6 +148,16 @@ export class UnifiedPlayer {
|
|
|
137
148
|
this.startTimeupdateLoop();
|
|
138
149
|
this.options.target.addEventListener("ended", () => this.emitter.emit("ended", undefined));
|
|
139
150
|
this.emitter.emitSticky("ready", undefined);
|
|
151
|
+
const bootstrapElapsed = performance.now() - bootstrapStart;
|
|
152
|
+
dbg.info("bootstrap", `ready in ${bootstrapElapsed.toFixed(0)}ms`);
|
|
153
|
+
if (bootstrapElapsed > 5000) {
|
|
154
|
+
// eslint-disable-next-line no-console
|
|
155
|
+
console.warn(
|
|
156
|
+
"[avbridge:bootstrap]",
|
|
157
|
+
`total bootstrap time ${bootstrapElapsed.toFixed(0)}ms — unusually slow. ` +
|
|
158
|
+
`Enable globalThis.AVBRIDGE_DEBUG for a per-phase breakdown.`,
|
|
159
|
+
);
|
|
160
|
+
}
|
|
140
161
|
} catch (err) {
|
|
141
162
|
const e = err instanceof Error ? err : new Error(String(err));
|
|
142
163
|
this.diag.recordError(e);
|
package/src/probe/avi.ts
CHANGED
|
@@ -177,7 +177,9 @@ function ffmpegToAvbridgeVideo(name: string): VideoCodec {
|
|
|
177
177
|
case "mpeg2video": return "mpeg2";
|
|
178
178
|
case "mpeg1video": return "mpeg1";
|
|
179
179
|
case "theora": return "theora";
|
|
180
|
-
case "
|
|
180
|
+
case "rv10": return "rv10";
|
|
181
|
+
case "rv20": return "rv20";
|
|
182
|
+
case "rv30": return "rv30";
|
|
181
183
|
case "rv40": return "rv40";
|
|
182
184
|
default: return name as VideoCodec;
|
|
183
185
|
}
|
|
@@ -198,6 +200,11 @@ function ffmpegToAvbridgeAudio(name: string): AudioCodec {
|
|
|
198
200
|
case "wmav2": return "wmav2";
|
|
199
201
|
case "wmapro": return "wmapro";
|
|
200
202
|
case "alac": return "alac";
|
|
203
|
+
case "cook": return "cook";
|
|
204
|
+
case "ra_144": return "ra_144";
|
|
205
|
+
case "ra_288": return "ra_288";
|
|
206
|
+
case "sipr": return "sipr";
|
|
207
|
+
case "atrac3": return "atrac3";
|
|
201
208
|
default: return name as AudioCodec;
|
|
202
209
|
}
|
|
203
210
|
}
|
package/src/probe/index.ts
CHANGED
|
@@ -21,12 +21,16 @@ const MEDIABUNNY_CONTAINERS = new Set<ContainerKind>([
|
|
|
21
21
|
*
|
|
22
22
|
* Routing:
|
|
23
23
|
* 1. Sniff the magic header. Cheap, deterministic, ignores file extensions.
|
|
24
|
-
* 2. If the container is one mediabunny supports → mediabunny
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
24
|
+
* 2. If the container is one mediabunny supports → try mediabunny first
|
|
25
|
+
* (fast path — it's a single pass of WASM-free JS parsing). If mediabunny
|
|
26
|
+
* throws (e.g. an assertion on an unsupported sample entry like `mp4v`
|
|
27
|
+
* for MPEG-4 Part 2 in ISOBMFF, or an exotic MKV codec), fall through to
|
|
28
|
+
* libav.js which handles the long tail of codecs mediabunny doesn't.
|
|
29
|
+
* The combined-error case surfaces *both* failures so the user sees
|
|
30
|
+
* which path each step took.
|
|
31
|
+
* 3. If sniffing identifies AVI/ASF/FLV (or `unknown`) → libav.js directly.
|
|
32
|
+
* mediabunny can't read those containers at all, so there's no fast path
|
|
33
|
+
* to try.
|
|
30
34
|
*/
|
|
31
35
|
export async function probe(source: MediaInput): Promise<MediaContext> {
|
|
32
36
|
const normalized = await normalizeSource(source);
|
|
@@ -35,10 +39,27 @@ export async function probe(source: MediaInput): Promise<MediaContext> {
|
|
|
35
39
|
if (MEDIABUNNY_CONTAINERS.has(sniffed)) {
|
|
36
40
|
try {
|
|
37
41
|
return await probeWithMediabunny(normalized, sniffed);
|
|
38
|
-
} catch (
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
} catch (mediabunnyErr) {
|
|
43
|
+
// mediabunny rejected the file. Before giving up, try libav — it can
|
|
44
|
+
// demux a much wider range of codec combinations in ISOBMFF/MKV/etc.
|
|
45
|
+
// than mediabunny's pure-JS parser (e.g. mp4v, wmv3-in-asf, flac in
|
|
46
|
+
// an MP4 container). This is "escalation", not "masking": if libav
|
|
47
|
+
// also fails we surface both errors below.
|
|
48
|
+
// eslint-disable-next-line no-console
|
|
49
|
+
console.warn(
|
|
50
|
+
`[avbridge] mediabunny rejected ${sniffed} file, falling back to libav:`,
|
|
51
|
+
(mediabunnyErr as Error).message,
|
|
41
52
|
);
|
|
53
|
+
try {
|
|
54
|
+
const { probeWithLibav } = await import("./avi.js");
|
|
55
|
+
return await probeWithLibav(normalized, sniffed);
|
|
56
|
+
} catch (libavErr) {
|
|
57
|
+
const mbMsg = (mediabunnyErr as Error).message || String(mediabunnyErr);
|
|
58
|
+
const lvMsg = libavErr instanceof Error ? libavErr.message : String(libavErr);
|
|
59
|
+
throw new Error(
|
|
60
|
+
`failed to probe ${sniffed} file. mediabunny: ${mbMsg}. libav fallback: ${lvMsg}.`,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
42
63
|
}
|
|
43
64
|
}
|
|
44
65
|
|
|
@@ -172,9 +172,31 @@ export class AudioOutput implements ClockSource {
|
|
|
172
172
|
node.connect(this.gain);
|
|
173
173
|
|
|
174
174
|
// Convert media time → ctx time using the anchor.
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
175
|
+
let ctxStart = this.ctxTimeAtAnchor + (this.mediaTimeOfNext - this.mediaTimeOfAnchor);
|
|
176
|
+
|
|
177
|
+
// When the decoder is slower than realtime, `ctxStart` falls into
|
|
178
|
+
// the past (ctx.currentTime has already passed it). Clamping each
|
|
179
|
+
// sample to `ctx.currentTime` individually (the old behavior)
|
|
180
|
+
// caused every stale sample in a burst to start at *the same
|
|
181
|
+
// instant*, stacking them on top of each other — the audible
|
|
182
|
+
// symptom was a series of clicks / a chord of stuttering cook
|
|
183
|
+
// packets.
|
|
184
|
+
//
|
|
185
|
+
// Correct behavior: when the first sample of a burst is behind,
|
|
186
|
+
// *rebase the anchor forward* so ctxStart = ctx.currentTime now.
|
|
187
|
+
// Subsequent samples in the same burst then schedule at
|
|
188
|
+
// ctxStart + offset as usual, laying out sequentially on the
|
|
189
|
+
// timeline instead of piling up. The downside is a visible jump
|
|
190
|
+
// in the audio clock — but the alternative was silent corruption.
|
|
191
|
+
// `now()` readers (the video renderer) just see the clock step
|
|
192
|
+
// forward and drop any frames older than the new time.
|
|
193
|
+
if (ctxStart < this.ctx.currentTime) {
|
|
194
|
+
this.ctxTimeAtAnchor = this.ctx.currentTime;
|
|
195
|
+
this.mediaTimeOfAnchor = this.mediaTimeOfNext;
|
|
196
|
+
ctxStart = this.ctx.currentTime;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
node.start(ctxStart);
|
|
178
200
|
|
|
179
201
|
this.mediaTimeOfNext += frameCount / sampleRate;
|
|
180
202
|
this.framesScheduled++;
|