avbridge 2.1.2 → 2.2.1
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 +138 -0
- package/README.md +98 -71
- 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-3AUGRKPY.js → chunk-DMWARSEF.js} +160 -27
- package/dist/chunk-DMWARSEF.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-DPVIOYGC.cjs → chunk-UF2N5L63.cjs} +164 -31
- package/dist/chunk-UF2N5L63.cjs.map +1 -0
- package/dist/element-browser.js +276 -21
- package/dist/element-browser.js.map +1 -1
- package/dist/element.cjs +4 -4
- package/dist/element.d.cts +1 -1
- package/dist/element.d.ts +1 -1
- package/dist/element.js +3 -3
- package/dist/index.cjs +18 -18
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- 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-U2NPmFvA.d.cts} +4 -3
- package/dist/{player-BdtUG4rh.d.ts → player-U2NPmFvA.d.ts} +4 -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/player.ts +46 -17
- package/src/probe/avi.ts +8 -1
- package/src/strategies/fallback/audio-output.ts +25 -3
- package/src/strategies/fallback/decoder.ts +96 -8
- package/src/strategies/fallback/index.ts +98 -6
- package/src/strategies/fallback/libav-loader.ts +12 -0
- package/src/strategies/fallback/video-renderer.ts +5 -1
- package/src/strategies/hybrid/index.ts +9 -1
- package/src/strategies/remux/index.ts +13 -1
- package/src/strategies/remux/pipeline.ts +6 -0
- 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-3AUGRKPY.js.map +0 -1
- package/dist/chunk-DPVIOYGC.cjs.map +0 -1
- package/dist/chunk-EJH67FXG.js.map +0 -1
- package/dist/chunk-JQH6D4OE.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/element.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
require('./chunk-
|
|
5
|
-
require('./chunk-
|
|
3
|
+
var chunkUF2N5L63_cjs = require('./chunk-UF2N5L63.cjs');
|
|
4
|
+
require('./chunk-HZLQNKFN.cjs');
|
|
5
|
+
require('./chunk-G4APZMCP.cjs');
|
|
6
6
|
require('./chunk-NZU7W256.cjs');
|
|
7
7
|
|
|
8
8
|
// src/element/avbridge-video.ts
|
|
@@ -226,7 +226,7 @@ var AvbridgeVideoElement = class extends HTMLElementCtor {
|
|
|
226
226
|
this._dispatch("loadstart", {});
|
|
227
227
|
let player;
|
|
228
228
|
try {
|
|
229
|
-
player = await
|
|
229
|
+
player = await chunkUF2N5L63_cjs.createPlayer({
|
|
230
230
|
source,
|
|
231
231
|
target: this._videoEl,
|
|
232
232
|
// Honor the consumer's preferred initial strategy. "auto" means
|
package/dist/element.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as MediaInput, m as StrategyName, S as StrategyClass, U as UnifiedPlayer, d as AudioTrackInfo, n as SubtitleTrackInfo, D as DiagnosticsSnapshot } from './player-
|
|
1
|
+
import { a as MediaInput, m as StrategyName, S as StrategyClass, U as UnifiedPlayer, d as AudioTrackInfo, n as SubtitleTrackInfo, D as DiagnosticsSnapshot } from './player-U2NPmFvA.cjs';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* `<avbridge-video>` — `HTMLMediaElement`-compatible primitive backed by the
|
package/dist/element.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as MediaInput, m as StrategyName, S as StrategyClass, U as UnifiedPlayer, d as AudioTrackInfo, n as SubtitleTrackInfo, D as DiagnosticsSnapshot } from './player-
|
|
1
|
+
import { a as MediaInput, m as StrategyName, S as StrategyClass, U as UnifiedPlayer, d as AudioTrackInfo, n as SubtitleTrackInfo, D as DiagnosticsSnapshot } from './player-U2NPmFvA.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* `<avbridge-video>` — `HTMLMediaElement`-compatible primitive backed by the
|
package/dist/element.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { createPlayer } from './chunk-
|
|
2
|
-
import './chunk-
|
|
3
|
-
import './chunk-
|
|
1
|
+
import { createPlayer } from './chunk-DMWARSEF.js';
|
|
2
|
+
import './chunk-ILKDNBSE.js';
|
|
3
|
+
import './chunk-5DMTJVIU.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
|
|
4
|
-
var
|
|
3
|
+
var chunkUF2N5L63_cjs = require('./chunk-UF2N5L63.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 chunkUF2N5L63_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 = chunkUF2N5L63_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 = chunkUF2N5L63_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 chunkUF2N5L63_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 ? chunkUF2N5L63_cjs.avbridgeVideoToMediabunny(videoTrackInfo.codec) : null;
|
|
150
|
+
const mbAudioCodec = audioTrackInfo ? chunkUF2N5L63_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 chunkUF2N5L63_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 chunkUF2N5L63_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 chunkUF2N5L63_cjs.UnifiedPlayer; }
|
|
556
556
|
});
|
|
557
557
|
Object.defineProperty(exports, "classify", {
|
|
558
558
|
enumerable: true,
|
|
559
|
-
get: function () { return
|
|
559
|
+
get: function () { return chunkUF2N5L63_cjs.classifyContext; }
|
|
560
560
|
});
|
|
561
561
|
Object.defineProperty(exports, "createPlayer", {
|
|
562
562
|
enumerable: true,
|
|
563
|
-
get: function () { return
|
|
563
|
+
get: function () { return chunkUF2N5L63_cjs.createPlayer; }
|
|
564
564
|
});
|
|
565
565
|
Object.defineProperty(exports, "probe", {
|
|
566
566
|
enumerable: true,
|
|
567
|
-
get: function () { return
|
|
567
|
+
get: function () { return chunkUF2N5L63_cjs.probe; }
|
|
568
568
|
});
|
|
569
569
|
Object.defineProperty(exports, "srtToVtt", {
|
|
570
570
|
enumerable: true,
|
|
571
|
-
get: function () { return
|
|
571
|
+
get: function () { return chunkUF2N5L63_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-U2NPmFvA.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-U2NPmFvA.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Pure classification — no I/O, no async. Test-friendly.
|
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-U2NPmFvA.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-U2NPmFvA.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Pure classification — no I/O, no async. Test-friendly.
|
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-DMWARSEF.js';
|
|
2
|
+
export { UnifiedPlayer, classifyContext as classify, createPlayer, probe, srtToVtt } from './chunk-DMWARSEF.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;
|
|
@@ -324,6 +324,7 @@ declare class UnifiedPlayer {
|
|
|
324
324
|
private lastProgressTime;
|
|
325
325
|
private lastProgressPosition;
|
|
326
326
|
private errorListener;
|
|
327
|
+
private endedListener;
|
|
327
328
|
private switchingPromise;
|
|
328
329
|
private subtitleResources;
|
|
329
330
|
/**
|
|
@@ -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;
|
|
@@ -324,6 +324,7 @@ declare class UnifiedPlayer {
|
|
|
324
324
|
private lastProgressTime;
|
|
325
325
|
private lastProgressPosition;
|
|
326
326
|
private errorListener;
|
|
327
|
+
private endedListener;
|
|
327
328
|
private switchingPromise;
|
|
328
329
|
private subtitleResources;
|
|
329
330
|
/**
|
|
@@ -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
|
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,
|
|
@@ -33,6 +34,11 @@ export class UnifiedPlayer {
|
|
|
33
34
|
private lastProgressPosition = -1;
|
|
34
35
|
private errorListener: (() => void) | null = null;
|
|
35
36
|
|
|
37
|
+
// Bound so we can removeEventListener in destroy(); without this the
|
|
38
|
+
// listener outlives the player and accumulates on elements that swap
|
|
39
|
+
// source (e.g. <avbridge-video>).
|
|
40
|
+
private endedListener: (() => void) | null = null;
|
|
41
|
+
|
|
36
42
|
// Serializes escalation / setStrategy calls
|
|
37
43
|
private switchingPromise: Promise<void> = Promise.resolve();
|
|
38
44
|
|
|
@@ -65,8 +71,14 @@ export class UnifiedPlayer {
|
|
|
65
71
|
}
|
|
66
72
|
|
|
67
73
|
private async bootstrap(): Promise<void> {
|
|
74
|
+
const bootstrapStart = performance.now();
|
|
68
75
|
try {
|
|
69
|
-
|
|
76
|
+
dbg.info("bootstrap", "start");
|
|
77
|
+
const ctx = await dbg.timed("probe", "probe", 3000, () => probe(this.options.source));
|
|
78
|
+
dbg.info("probe",
|
|
79
|
+
`container=${ctx.container} video=${ctx.videoTracks[0]?.codec ?? "-"} ` +
|
|
80
|
+
`audio=${ctx.audioTracks[0]?.codec ?? "-"} probedBy=${ctx.probedBy}`,
|
|
81
|
+
);
|
|
70
82
|
this.diag.recordProbe(ctx);
|
|
71
83
|
this.mediaContext = ctx;
|
|
72
84
|
|
|
@@ -99,6 +111,10 @@ export class UnifiedPlayer {
|
|
|
99
111
|
const decision = this.options.initialStrategy
|
|
100
112
|
? buildInitialDecision(this.options.initialStrategy, ctx)
|
|
101
113
|
: classify(ctx);
|
|
114
|
+
dbg.info("classify",
|
|
115
|
+
`strategy=${decision.strategy} class=${decision.class} reason="${decision.reason}"` +
|
|
116
|
+
(decision.fallbackChain ? ` fallback=${decision.fallbackChain.join("→")}` : ""),
|
|
117
|
+
);
|
|
102
118
|
this.classification = decision;
|
|
103
119
|
this.diag.recordClassification(decision);
|
|
104
120
|
|
|
@@ -110,12 +126,10 @@ export class UnifiedPlayer {
|
|
|
110
126
|
// Try the primary strategy, falling through the chain on failure
|
|
111
127
|
await this.startSession(decision.strategy, decision.reason);
|
|
112
128
|
|
|
113
|
-
// Apply subtitles for non-canvas strategies.
|
|
114
|
-
//
|
|
115
|
-
//
|
|
116
|
-
//
|
|
117
|
-
// load-bearing for playback. (Promoting this to a typed `subtitleerror`
|
|
118
|
-
// event is a nice-to-have follow-up.)
|
|
129
|
+
// Apply subtitles for non-canvas strategies. Per-track failures are
|
|
130
|
+
// caught inside attachSubtitleTracks and logged via console.warn —
|
|
131
|
+
// subtitles are not load-bearing, so a bad sidecar must not break
|
|
132
|
+
// bootstrap.
|
|
119
133
|
if (this.session!.strategy !== "fallback" && this.session!.strategy !== "hybrid") {
|
|
120
134
|
await attachSubtitleTracks(
|
|
121
135
|
this.options.target,
|
|
@@ -135,8 +149,19 @@ export class UnifiedPlayer {
|
|
|
135
149
|
});
|
|
136
150
|
|
|
137
151
|
this.startTimeupdateLoop();
|
|
138
|
-
this.
|
|
152
|
+
this.endedListener = () => this.emitter.emit("ended", undefined);
|
|
153
|
+
this.options.target.addEventListener("ended", this.endedListener);
|
|
139
154
|
this.emitter.emitSticky("ready", undefined);
|
|
155
|
+
const bootstrapElapsed = performance.now() - bootstrapStart;
|
|
156
|
+
dbg.info("bootstrap", `ready in ${bootstrapElapsed.toFixed(0)}ms`);
|
|
157
|
+
if (bootstrapElapsed > 5000) {
|
|
158
|
+
// eslint-disable-next-line no-console
|
|
159
|
+
console.warn(
|
|
160
|
+
"[avbridge:bootstrap]",
|
|
161
|
+
`total bootstrap time ${bootstrapElapsed.toFixed(0)}ms — unusually slow. ` +
|
|
162
|
+
`Enable globalThis.AVBRIDGE_DEBUG for a per-phase breakdown.`,
|
|
163
|
+
);
|
|
164
|
+
}
|
|
140
165
|
} catch (err) {
|
|
141
166
|
const e = err instanceof Error ? err : new Error(String(err));
|
|
142
167
|
this.diag.recordError(e);
|
|
@@ -471,6 +496,10 @@ export class UnifiedPlayer {
|
|
|
471
496
|
this.timeupdateInterval = null;
|
|
472
497
|
}
|
|
473
498
|
this.clearSupervisor();
|
|
499
|
+
if (this.endedListener) {
|
|
500
|
+
this.options.target.removeEventListener("ended", this.endedListener);
|
|
501
|
+
this.endedListener = null;
|
|
502
|
+
}
|
|
474
503
|
if (this.session) {
|
|
475
504
|
await this.session.destroy();
|
|
476
505
|
this.session = null;
|
|
@@ -487,14 +516,12 @@ export async function createPlayer(options: CreatePlayerOptions): Promise<Unifie
|
|
|
487
516
|
}
|
|
488
517
|
|
|
489
518
|
/**
|
|
490
|
-
* Build a synthetic classification
|
|
491
|
-
*
|
|
492
|
-
*
|
|
493
|
-
*
|
|
494
|
-
*
|
|
495
|
-
*
|
|
496
|
-
* any downstream logic that trusted `strategyClass` could make the wrong
|
|
497
|
-
* decision. We now derive the class from the picked strategy.
|
|
519
|
+
* Build a synthetic classification for an explicit `initialStrategy` override.
|
|
520
|
+
* The `class` is derived from the chosen strategy so diagnostics and any
|
|
521
|
+
* downstream consumer of `strategyClass` see the real strategy. The fallback
|
|
522
|
+
* chain is inherited from the natural classification but must never contain
|
|
523
|
+
* `initial` itself — otherwise `startSession` would retry the strategy that
|
|
524
|
+
* just failed before escalating.
|
|
498
525
|
*
|
|
499
526
|
* @internal — exported for unit tests; not part of the public API.
|
|
500
527
|
*/
|
|
@@ -504,11 +531,13 @@ export function buildInitialDecision(
|
|
|
504
531
|
): Classification {
|
|
505
532
|
const natural = classify(ctx);
|
|
506
533
|
const cls = strategyToClass(initial, natural);
|
|
534
|
+
const inherited = natural.fallbackChain ?? defaultFallbackChain(initial);
|
|
535
|
+
const fallbackChain = inherited.filter((s) => s !== initial);
|
|
507
536
|
return {
|
|
508
537
|
class: cls,
|
|
509
538
|
strategy: initial,
|
|
510
539
|
reason: `initial strategy "${initial}" requested via options.initialStrategy`,
|
|
511
|
-
fallbackChain
|
|
540
|
+
fallbackChain,
|
|
512
541
|
};
|
|
513
542
|
}
|
|
514
543
|
|
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
|
}
|
|
@@ -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++;
|