avbridge 2.1.2 → 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 +93 -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-3AUGRKPY.js → chunk-C5VA5U5O.js} +94 -16
- 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-DPVIOYGC.cjs → chunk-OE66B34H.cjs} +98 -20
- package/dist/chunk-OE66B34H.cjs.map +1 -0
- package/dist/element-browser.js +210 -10
- 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-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/player.ts +22 -1
- 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 +90 -6
- package/src/strategies/fallback/libav-loader.ts +12 -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 chunkOE66B34H_cjs = require('./chunk-OE66B34H.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 chunkOE66B34H_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-DUyvltvy.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-DUyvltvy.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-C5VA5U5O.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 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.
|
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.
|
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
|
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
|
}
|
|
@@ -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++;
|
|
@@ -135,6 +135,20 @@ export async function startDecoder(opts: StartDecoderOptions): Promise<DecoderHa
|
|
|
135
135
|
let videoFramesDecoded = 0;
|
|
136
136
|
let audioFramesDecoded = 0;
|
|
137
137
|
|
|
138
|
+
// Decode-rate watchdog. Samples framesDecoded every second and
|
|
139
|
+
// compares against realtime expected frames for the source fps. If
|
|
140
|
+
// the decoder sustains less than 60% of realtime for more than
|
|
141
|
+
// 5 seconds (counting only time since the first frame emerged),
|
|
142
|
+
// emits a one-shot diagnostic so users know why playback is
|
|
143
|
+
// stuttering instead of guessing. A second one-shot fires if the
|
|
144
|
+
// renderer's overflow-drop rate exceeds 10% of decoded frames —
|
|
145
|
+
// that symptom means the decoder is BURSTING faster than the
|
|
146
|
+
// renderer can drain, which is a different bug from "decoder slow".
|
|
147
|
+
let watchdogFirstFrameMs = 0;
|
|
148
|
+
let watchdogSlowSinceMs = 0;
|
|
149
|
+
let watchdogSlowWarned = false;
|
|
150
|
+
let watchdogOverflowWarned = false;
|
|
151
|
+
|
|
138
152
|
// Synthetic timestamp counters. Reset on seek.
|
|
139
153
|
let syntheticVideoUs = 0;
|
|
140
154
|
let syntheticAudioUs = 0;
|
|
@@ -150,10 +164,18 @@ export async function startDecoder(opts: StartDecoderOptions): Promise<DecoderHa
|
|
|
150
164
|
let readErr: number;
|
|
151
165
|
let packets: Record<number, LibavPacket[]>;
|
|
152
166
|
try {
|
|
153
|
-
//
|
|
154
|
-
//
|
|
155
|
-
//
|
|
156
|
-
//
|
|
167
|
+
// Batch size tunes the tradeoff between JS↔WASM call overhead
|
|
168
|
+
// (small = more crossings per second) and queue burstiness
|
|
169
|
+
// (large = decoder hands the renderer big bursts at once that
|
|
170
|
+
// can blow past the renderer's 64-frame hard cap before the
|
|
171
|
+
// per-batch `queueHighWater` throttle runs).
|
|
172
|
+
//
|
|
173
|
+
// We tried 64 KB and saw ~30% overflow drops on RMVB:rv40 at
|
|
174
|
+
// 1024x768 because one decode batch regularly produced >30
|
|
175
|
+
// frames. 16 KB keeps each batch ≈ 4-6 video packets at
|
|
176
|
+
// typical bitrates, so the worst-case queue spike stays under
|
|
177
|
+
// `queueHighWater` and the throttle has a chance to apply
|
|
178
|
+
// backpressure *between* batches rather than within one.
|
|
157
179
|
[readErr, packets] = await libav.ff_read_frame_multi(fmt_ctx, readPkt, {
|
|
158
180
|
limit: 16 * 1024,
|
|
159
181
|
});
|
|
@@ -167,16 +189,82 @@ export async function startDecoder(opts: StartDecoderOptions): Promise<DecoderHa
|
|
|
167
189
|
const videoPackets = videoStream ? packets[videoStream.index] : undefined;
|
|
168
190
|
const audioPackets = audioStream ? packets[audioStream.index] : undefined;
|
|
169
191
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
192
|
+
// Decode audio BEFORE video. On software-decode-bound content
|
|
193
|
+
// (rv40/mpeg4/wmv3 @ 720p+) a single video batch can take
|
|
194
|
+
// 200-400 ms of wall time; if the scheduler hasn't been fed
|
|
195
|
+
// during that window, audio output runs dry and the user hears
|
|
196
|
+
// clicks/gaps. Audio is time-critical; video can drop a frame
|
|
197
|
+
// and nobody notices. Audio decode is also typically <1 ms per
|
|
198
|
+
// packet for cook/mp3/aac, so doing it first barely delays
|
|
199
|
+
// video decoding at all.
|
|
174
200
|
if (audioDec && audioPackets && audioPackets.length > 0) {
|
|
175
201
|
await decodeAudioBatch(audioPackets, myToken);
|
|
176
202
|
}
|
|
203
|
+
if (myToken !== pumpToken || destroyed) return;
|
|
204
|
+
if (videoDec && videoPackets && videoPackets.length > 0) {
|
|
205
|
+
await decodeVideoBatch(videoPackets, myToken);
|
|
206
|
+
}
|
|
177
207
|
|
|
178
208
|
packetsRead += (videoPackets?.length ?? 0) + (audioPackets?.length ?? 0);
|
|
179
209
|
|
|
210
|
+
// ── Decode-rate watchdog ──────────────────────────────────────
|
|
211
|
+
if (videoFramesDecoded > 0) {
|
|
212
|
+
if (watchdogFirstFrameMs === 0) {
|
|
213
|
+
watchdogFirstFrameMs = performance.now();
|
|
214
|
+
}
|
|
215
|
+
const elapsedSinceFirst = (performance.now() - watchdogFirstFrameMs) / 1000;
|
|
216
|
+
|
|
217
|
+
// 1. Slow-decode detection (sustained <60% of realtime fps).
|
|
218
|
+
if (elapsedSinceFirst > 1 && !watchdogSlowWarned) {
|
|
219
|
+
const expectedFrames = elapsedSinceFirst * videoFps;
|
|
220
|
+
const ratio = videoFramesDecoded / expectedFrames;
|
|
221
|
+
if (ratio < 0.6) {
|
|
222
|
+
if (watchdogSlowSinceMs === 0) watchdogSlowSinceMs = performance.now();
|
|
223
|
+
if ((performance.now() - watchdogSlowSinceMs) / 1000 > 5) {
|
|
224
|
+
watchdogSlowWarned = true;
|
|
225
|
+
console.warn(
|
|
226
|
+
"[avbridge:decode-rate]",
|
|
227
|
+
`decoder is running slower than realtime: ` +
|
|
228
|
+
`${videoFramesDecoded} frames in ${elapsedSinceFirst.toFixed(1)}s ` +
|
|
229
|
+
`(${(videoFramesDecoded / elapsedSinceFirst).toFixed(1)} fps vs ${videoFps} fps source — ` +
|
|
230
|
+
`${(ratio * 100).toFixed(0)}% of realtime). ` +
|
|
231
|
+
`Playback will stutter. Typical causes: software decode of a codec with no WebCodecs support ` +
|
|
232
|
+
`(rv40, mpeg4 @ 720p+, wmv3), or a resolution too large for single-threaded WASM to keep up with.`,
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
} else {
|
|
236
|
+
watchdogSlowSinceMs = 0;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// 2. Overflow-drop detection (>10% of decoded frames dropped
|
|
241
|
+
// by the renderer's hard cap). This means the decoder
|
|
242
|
+
// produces BURSTS — it's fast enough on average but one
|
|
243
|
+
// batch delivers >30 frames at a time, overflowing before
|
|
244
|
+
// the queueHighWater throttle can apply backpressure.
|
|
245
|
+
// Symptom is different from "decoder slow": here the fps
|
|
246
|
+
// ratio looks fine but the user sees choppy playback.
|
|
247
|
+
if (
|
|
248
|
+
!watchdogOverflowWarned &&
|
|
249
|
+
videoFramesDecoded > 100 // wait for a meaningful sample
|
|
250
|
+
) {
|
|
251
|
+
const rendererStats = opts.renderer.stats() as { framesDroppedOverflow?: number };
|
|
252
|
+
const overflow = rendererStats.framesDroppedOverflow ?? 0;
|
|
253
|
+
if (overflow / videoFramesDecoded > 0.1) {
|
|
254
|
+
watchdogOverflowWarned = true;
|
|
255
|
+
console.warn(
|
|
256
|
+
"[avbridge:overflow-drop]",
|
|
257
|
+
`renderer is dropping ${overflow}/${videoFramesDecoded} frames ` +
|
|
258
|
+
`(${((overflow / videoFramesDecoded) * 100).toFixed(0)}%) because the decoder ` +
|
|
259
|
+
`is producing bursts faster than the canvas can drain. Symptom: choppy ` +
|
|
260
|
+
`playback despite decoder keeping up on average. Fix would be smaller ` +
|
|
261
|
+
`read batches in the pump loop or a lower queueHighWater cap — see ` +
|
|
262
|
+
`src/strategies/fallback/decoder.ts.`,
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
180
268
|
// Throttle: don't run too far ahead of playback. Two backpressure
|
|
181
269
|
// signals:
|
|
182
270
|
// - Audio buffer (mediaTimeOfNext - now()) > 2 sec — we have
|