avbridge 2.5.0 → 2.7.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 +85 -0
- package/dist/{chunk-TBW26OPP.cjs → chunk-6SOFJV44.cjs} +59 -2
- package/dist/chunk-6SOFJV44.cjs.map +1 -0
- package/dist/{chunk-KY2GPCT7.js → chunk-OGYHFY6K.js} +59 -2
- package/dist/chunk-OGYHFY6K.js.map +1 -0
- package/dist/element-browser.js +87 -2
- package/dist/element-browser.js.map +1 -1
- package/dist/element.cjs +32 -4
- package/dist/element.cjs.map +1 -1
- package/dist/element.d.cts +13 -1
- package/dist/element.d.ts +13 -1
- package/dist/element.js +31 -3
- package/dist/element.js.map +1 -1
- package/dist/index.cjs +8 -8
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/{player-B6WB74RD.d.cts → player-DGXeCNfD.d.cts} +41 -1
- package/dist/{player-B6WB74RD.d.ts → player-DGXeCNfD.d.ts} +41 -1
- package/dist/player.cjs +130 -2
- package/dist/player.cjs.map +1 -1
- package/dist/player.d.cts +191 -132
- package/dist/player.d.ts +191 -132
- package/dist/player.js +130 -2
- package/dist/player.js.map +1 -1
- package/package.json +7 -1
- package/src/element/avbridge-player.ts +84 -0
- package/src/element/avbridge-video.ts +86 -3
- package/src/element/player-styles.ts +12 -0
- package/src/strategies/fallback/index.ts +27 -0
- package/src/strategies/hybrid/index.ts +18 -0
- package/src/types.ts +32 -0
- package/src/util/time-ranges.ts +40 -0
- package/dist/chunk-KY2GPCT7.js.map +0 -1
- package/dist/chunk-TBW26OPP.cjs.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,91 @@ All notable changes to **avbridge.js** are documented here. The format follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project
|
|
5
5
|
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [2.7.0]
|
|
8
|
+
|
|
9
|
+
Cross-browser confidence release. A Playwright-based test tier now
|
|
10
|
+
validates that avbridge picks the right strategy on each major browser
|
|
11
|
+
engine. No runtime code changes — the release is tooling, fixtures, and
|
|
12
|
+
a new testing discipline.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **Cross-browser test tier (Playwright).** New `tests/browser/` directory
|
|
17
|
+
with the matrix run via `npm run test:browser` across Chromium, Firefox,
|
|
18
|
+
and WebKit. Initial slice — `fixtures.spec.ts` — validates that `probe()`
|
|
19
|
+
and `classify()` produce the expected output for each fixture on each
|
|
20
|
+
browser (15 tests, ~11s). Per-browser expectations live in
|
|
21
|
+
`tests/browser/_expectations.ts` so evolving browser codec support is
|
|
22
|
+
a one-file change. Playwright's `webServer` config auto-starts Vite;
|
|
23
|
+
the five existing Puppeteer scripts are unchanged and continue to
|
|
24
|
+
cover Chromium-only scenarios. `npm run test:browser:ui` for the
|
|
25
|
+
interactive trace viewer.
|
|
26
|
+
- New test harness page at `demo/tests-harness.html` that exposes the
|
|
27
|
+
avbridge API on `window` for `page.evaluate()`-style tests. Only served
|
|
28
|
+
in dev mode; not shipped in production builds.
|
|
29
|
+
- New fixture: `bbb-hevc-aac.mkv` (generated via `npm run fixtures`) for
|
|
30
|
+
exercising the HEVC strategy boundary across browsers.
|
|
31
|
+
- `docs/dev/TESTING.md` — documents the new Tier 4 alongside the
|
|
32
|
+
existing three tiers, with scope guidance (what belongs in `fixtures`,
|
|
33
|
+
`playback`, and `contract` spec files).
|
|
34
|
+
|
|
35
|
+
### Findings worth surfacing
|
|
36
|
+
|
|
37
|
+
- `classify()` is deliberately **browser-independent** for most codec
|
|
38
|
+
paths. Per-browser divergence surfaces at runtime via escalation, not
|
|
39
|
+
at classification. The test-tier split reflects this architecture:
|
|
40
|
+
`fixtures.spec.ts` for the deterministic decision, `playback.spec.ts`
|
|
41
|
+
(planned v2.7.1) for runtime escalation behavior.
|
|
42
|
+
|
|
43
|
+
### Coming in follow-ups
|
|
44
|
+
|
|
45
|
+
- **v2.7.1** — `playback.spec.ts`. Bootstrap → play → destroy per
|
|
46
|
+
fixture per browser. Catches runtime escalation (e.g. Firefox
|
|
47
|
+
escalating HEVC MKV from remux → fallback when MSE rejects hevc1.*).
|
|
48
|
+
- **v2.7.2** — `contract.spec.ts`. HTMLMediaElement event + property
|
|
49
|
+
parity across strategies and browsers.
|
|
50
|
+
|
|
51
|
+
## [2.6.0]
|
|
52
|
+
|
|
53
|
+
`<avbridge-player>` polish release. Four targeted ergonomics upgrades.
|
|
54
|
+
|
|
55
|
+
### Added
|
|
56
|
+
|
|
57
|
+
- **Typed `addEventListener` / `removeEventListener` overloads** on both
|
|
58
|
+
`<avbridge-video>` and `<avbridge-player>`. Consumers using avbridge
|
|
59
|
+
custom events (`ready`, `strategychange`, `trackschange`, `timeupdate`,
|
|
60
|
+
`error`, etc.) now receive a typed `CustomEvent<Detail>` without the
|
|
61
|
+
`as unknown as CustomEvent` cast tax. Standard HTMLMediaElement events
|
|
62
|
+
(`play`, `pause`, `seeking`, etc.) retain their native typing via
|
|
63
|
+
`HTMLElementEventMap`. New type: `AvbridgeVideoElementEventMap`.
|
|
64
|
+
- **Drag-and-drop file input** on `<avbridge-player>`. Drop a video file
|
|
65
|
+
onto the player and it loads + plays, matching the demo's file-picker
|
|
66
|
+
flow. Visual dashed-border feedback during dragover (stylable via
|
|
67
|
+
`.avp-dragover`).
|
|
68
|
+
- **`<track>` children parsing.** Light-DOM `<track src="subs.vtt"
|
|
69
|
+
srclang="en">` children declared inside `<avbridge-player>` or
|
|
70
|
+
`<avbridge-video>` were already cloned into the shadow `<video>` for
|
|
71
|
+
native/remux strategies; they now also populate the subtitle list that
|
|
72
|
+
the player's settings menu renders. HTML-declared tracks get stable
|
|
73
|
+
IDs in the 10000+ range to avoid colliding with container-embedded
|
|
74
|
+
IDs. MutationObserver-driven — add or remove a `<track>` at any time
|
|
75
|
+
and the menu updates.
|
|
76
|
+
- **HTMLMediaElement parity — `readyState` and `seekable`** on canvas
|
|
77
|
+
strategies. Previously the inner `<video>` (with no `src`) returned
|
|
78
|
+
`readyState: 0` and empty `seekable` ranges for hybrid/fallback.
|
|
79
|
+
Now synthesized: `readyState` reflects frame+audio readiness,
|
|
80
|
+
`seekable` spans `[0, duration]` once probe completes. `buffered`
|
|
81
|
+
and `networkState` remain deferred — both need meaningful transport
|
|
82
|
+
state machinery.
|
|
83
|
+
|
|
84
|
+
### Deprecated / Deferred
|
|
85
|
+
|
|
86
|
+
- `buffered` on canvas strategies still returns empty TimeRanges. Requires
|
|
87
|
+
decoder-position → media-time plumbing; tracked for a follow-up.
|
|
88
|
+
- `networkState` not yet exposed on the element. Needs a transport
|
|
89
|
+
state machine spanning probe → libav reader → decoder; out of scope
|
|
90
|
+
for this release.
|
|
91
|
+
|
|
7
92
|
## [2.5.0]
|
|
8
93
|
|
|
9
94
|
The "legacy transcode breadth" release. avbridge.js can now transcode
|
|
@@ -1990,6 +1990,35 @@ async function loadBridge() {
|
|
|
1990
1990
|
}
|
|
1991
1991
|
}
|
|
1992
1992
|
|
|
1993
|
+
// src/util/time-ranges.ts
|
|
1994
|
+
function makeTimeRanges(ranges) {
|
|
1995
|
+
const frozen = ranges.slice();
|
|
1996
|
+
const impl = {
|
|
1997
|
+
get length() {
|
|
1998
|
+
return frozen.length;
|
|
1999
|
+
},
|
|
2000
|
+
start(index) {
|
|
2001
|
+
if (index < 0 || index >= frozen.length) {
|
|
2002
|
+
throw new DOMException(
|
|
2003
|
+
`TimeRanges.start: index ${index} out of range (length=${frozen.length})`,
|
|
2004
|
+
"IndexSizeError"
|
|
2005
|
+
);
|
|
2006
|
+
}
|
|
2007
|
+
return frozen[index][0];
|
|
2008
|
+
},
|
|
2009
|
+
end(index) {
|
|
2010
|
+
if (index < 0 || index >= frozen.length) {
|
|
2011
|
+
throw new DOMException(
|
|
2012
|
+
`TimeRanges.end: index ${index} out of range (length=${frozen.length})`,
|
|
2013
|
+
"IndexSizeError"
|
|
2014
|
+
);
|
|
2015
|
+
}
|
|
2016
|
+
return frozen[index][1];
|
|
2017
|
+
}
|
|
2018
|
+
};
|
|
2019
|
+
return impl;
|
|
2020
|
+
}
|
|
2021
|
+
|
|
1993
2022
|
// src/strategies/hybrid/index.ts
|
|
1994
2023
|
var READY_AUDIO_BUFFER_SECONDS = 0.3;
|
|
1995
2024
|
var READY_TIMEOUT_SECONDS = 10;
|
|
@@ -2047,6 +2076,18 @@ async function createHybridSession(ctx, target, transport) {
|
|
|
2047
2076
|
get: () => ctx.duration ?? NaN
|
|
2048
2077
|
});
|
|
2049
2078
|
}
|
|
2079
|
+
Object.defineProperty(target, "readyState", {
|
|
2080
|
+
configurable: true,
|
|
2081
|
+
get: () => {
|
|
2082
|
+
if (!renderer.hasFrames()) return 0;
|
|
2083
|
+
if (!audio.isPlaying() && audio.bufferAhead() <= 0 && !audio.isNoAudio()) return 1;
|
|
2084
|
+
return 2;
|
|
2085
|
+
}
|
|
2086
|
+
});
|
|
2087
|
+
Object.defineProperty(target, "seekable", {
|
|
2088
|
+
configurable: true,
|
|
2089
|
+
get: () => makeTimeRanges(ctx.duration && Number.isFinite(ctx.duration) && ctx.duration > 0 ? [[0, ctx.duration]] : [])
|
|
2090
|
+
});
|
|
2050
2091
|
async function waitForBuffer() {
|
|
2051
2092
|
const start = performance.now();
|
|
2052
2093
|
while (true) {
|
|
@@ -2128,6 +2169,8 @@ async function createHybridSession(ctx, target, transport) {
|
|
|
2128
2169
|
delete target.paused;
|
|
2129
2170
|
delete target.volume;
|
|
2130
2171
|
delete target.muted;
|
|
2172
|
+
delete target.readyState;
|
|
2173
|
+
delete target.seekable;
|
|
2131
2174
|
} catch {
|
|
2132
2175
|
}
|
|
2133
2176
|
},
|
|
@@ -2648,6 +2691,18 @@ async function createFallbackSession(ctx, target, transport) {
|
|
|
2648
2691
|
get: () => ctx.duration ?? NaN
|
|
2649
2692
|
});
|
|
2650
2693
|
}
|
|
2694
|
+
Object.defineProperty(target, "readyState", {
|
|
2695
|
+
configurable: true,
|
|
2696
|
+
get: () => {
|
|
2697
|
+
if (!renderer.hasFrames()) return 0;
|
|
2698
|
+
if (!audio.isPlaying() && audio.bufferAhead() <= 0 && !audio.isNoAudio()) return 1;
|
|
2699
|
+
return 2;
|
|
2700
|
+
}
|
|
2701
|
+
});
|
|
2702
|
+
Object.defineProperty(target, "seekable", {
|
|
2703
|
+
configurable: true,
|
|
2704
|
+
get: () => makeTimeRanges(ctx.duration && Number.isFinite(ctx.duration) && ctx.duration > 0 ? [[0, ctx.duration]] : [])
|
|
2705
|
+
});
|
|
2651
2706
|
async function waitForBuffer() {
|
|
2652
2707
|
const start = performance.now();
|
|
2653
2708
|
let firstFrameAtMs = 0;
|
|
@@ -2750,6 +2805,8 @@ async function createFallbackSession(ctx, target, transport) {
|
|
|
2750
2805
|
delete target.paused;
|
|
2751
2806
|
delete target.volume;
|
|
2752
2807
|
delete target.muted;
|
|
2808
|
+
delete target.readyState;
|
|
2809
|
+
delete target.seekable;
|
|
2753
2810
|
} catch {
|
|
2754
2811
|
}
|
|
2755
2812
|
},
|
|
@@ -3309,5 +3366,5 @@ exports.NATIVE_VIDEO_CODECS = NATIVE_VIDEO_CODECS;
|
|
|
3309
3366
|
exports.UnifiedPlayer = UnifiedPlayer;
|
|
3310
3367
|
exports.classifyContext = classifyContext;
|
|
3311
3368
|
exports.createPlayer = createPlayer;
|
|
3312
|
-
//# sourceMappingURL=chunk-
|
|
3313
|
-
//# sourceMappingURL=chunk-
|
|
3369
|
+
//# sourceMappingURL=chunk-6SOFJV44.cjs.map
|
|
3370
|
+
//# sourceMappingURL=chunk-6SOFJV44.cjs.map
|