avbridge 2.1.0 → 2.1.2

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 CHANGED
@@ -4,6 +4,62 @@ All notable changes to **avbridge** 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.1.2]
8
+
9
+ ### Fixed
10
+
11
+ - **Fallback strategy video now visible inside `<avbridge-video>`.** The
12
+ fallback renderer attaches its canvas overlay via `target.parentElement`,
13
+ but when the `<video>` lives inside a `ShadowRoot` (as it does in the
14
+ custom element), `parentElement` is `null` because `ShadowRoot` is not
15
+ an `Element`. The canvas silently never got attached to the DOM, so
16
+ frames were decoded and "painted" (the stats counters incremented) but
17
+ nothing was ever visible — just audio. Fixed by wrapping the shadow
18
+ `<video>` in a positioned `<div part="stage">` and hardening the
19
+ renderer's parent lookup to use `parentNode` as a fallback with a loud
20
+ warning if no parent exists at all.
21
+
22
+ - **Remux strategy reseek no longer fails with "First packet must be a
23
+ key packet".** When `setStrategy("remux")` is invoked mid-playback, the
24
+ pipeline recreates mediabunny's `Output` (required because mediabunny's
25
+ fMP4 muxer is one-shot streaming). The pump's packet race could then
26
+ emit an audio packet first on the fresh muxer, which mediabunny rejects
27
+ because the first packet of any muxer run must be a key packet. The
28
+ pump now forces the first video packet — which we fetch via
29
+ `getKeyPacket()` and is guaranteed to be a keyframe — out before any
30
+ audio.
31
+
32
+ - **`mp4v`-in-MP4 files now fall through to libav probing.** mediabunny's
33
+ MP4 demuxer asserts on files whose video sample entry type isn't one
34
+ it recognizes (`mp4v` for MPEG-4 Part 2 / DivX / Xvid packaged in
35
+ ISOBMFF is the common case). Previously the probe rethrew the
36
+ assertion and gave up. The probe now escalates to libav when
37
+ mediabunny fails on any mediabunny-targeted container (mp4, mkv,
38
+ webm, …), which handles the long tail of codec combinations
39
+ mediabunny's pure-JS parser doesn't cover. If libav also fails, both
40
+ errors are surfaced together.
41
+
42
+ - **Demo dev server serves libav binaries via a Vite middleware plugin**
43
+ instead of `demo/public/libav/`. Recent Vite versions refuse to let
44
+ source code `import()` files out of `public/`, which broke the libav
45
+ loader's dynamic import. `serveVendorLibav()` in `vite.config.ts` now
46
+ streams files directly out of `vendor/libav/` at the same `/libav/*`
47
+ URL, bypassing the restriction. `scripts/copy-libav.mjs` is
48
+ simplified — it no longer mirrors to the demo's public tree.
49
+
50
+ ## [2.1.1]
51
+
52
+ ### Fixed
53
+
54
+ - **`dist/element-browser.js` no longer has a bare `libavjs-webcodecs-bridge`
55
+ import.** 2.1.0 inlined mediabunny into the browser bundle but left
56
+ `libavjs-webcodecs-bridge` external, so direct
57
+ `<script type="module">` consumers hit
58
+ `Failed to resolve module specifier "libavjs-webcodecs-bridge"` on load.
59
+ The browser entry now inlines it via `noExternal`. Only the actual
60
+ libav.js WASM variants stay external, and those are loaded via URL
61
+ dynamic imports relative to `import.meta.url`, not bare specifiers.
62
+
7
63
  ## [2.1.0]
8
64
 
9
65
  ### Added
@@ -184,10 +184,21 @@ async function probe(source) {
184
184
  if (MEDIABUNNY_CONTAINERS.has(sniffed)) {
185
185
  try {
186
186
  return await probeWithMediabunny(normalized, sniffed);
187
- } catch (err) {
188
- throw new Error(
189
- `mediabunny failed to probe a ${sniffed} file: ${err.message}`
187
+ } catch (mediabunnyErr) {
188
+ console.warn(
189
+ `[avbridge] mediabunny rejected ${sniffed} file, falling back to libav:`,
190
+ mediabunnyErr.message
190
191
  );
192
+ try {
193
+ const { probeWithLibav } = await import('./avi-V6HYQVR2.js');
194
+ return await probeWithLibav(normalized, sniffed);
195
+ } catch (libavErr) {
196
+ const mbMsg = mediabunnyErr.message || String(mediabunnyErr);
197
+ const lvMsg = libavErr instanceof Error ? libavErr.message : String(libavErr);
198
+ throw new Error(
199
+ `failed to probe ${sniffed} file. mediabunny: ${mbMsg}. libav fallback: ${lvMsg}.`
200
+ );
201
+ }
191
202
  }
192
203
  }
193
204
  try {
@@ -988,7 +999,8 @@ async function createRemuxPipeline(ctx, video) {
988
999
  if (destroyed || pumpToken !== token) break;
989
1000
  const vTs = !vNext.done ? vNext.value.timestamp : Number.POSITIVE_INFINITY;
990
1001
  const aTs = !aNext.done ? aNext.value.timestamp : Number.POSITIVE_INFINITY;
991
- if (!vNext.done && vTs <= aTs) {
1002
+ const forceVideoFirst = firstVideo && !vNext.done;
1003
+ if (!vNext.done && (forceVideoFirst || vTs <= aTs)) {
992
1004
  await videoSource.add(
993
1005
  vNext.value,
994
1006
  firstVideo && videoConfig ? { decoderConfig: videoConfig } : void 0
@@ -1126,11 +1138,20 @@ var VideoRenderer = class {
1126
1138
  });
1127
1139
  this.canvas = document.createElement("canvas");
1128
1140
  this.canvas.style.cssText = "position:absolute;left:0;top:0;width:100%;height:100%;background:black;";
1129
- const parent = target.parentElement;
1130
- if (parent && getComputedStyle(parent).position === "static") {
1131
- parent.style.position = "relative";
1141
+ const parent = target.parentElement ?? target.parentNode;
1142
+ if (parent && parent instanceof HTMLElement) {
1143
+ if (getComputedStyle(parent).position === "static") {
1144
+ parent.style.position = "relative";
1145
+ }
1146
+ }
1147
+ if (parent) {
1148
+ parent.insertBefore(this.canvas, target);
1149
+ } else {
1150
+ console.warn(
1151
+ "[avbridge] fallback renderer: target <video> has no parent; appending canvas to document.body as a fallback."
1152
+ );
1153
+ document.body.appendChild(this.canvas);
1132
1154
  }
1133
- parent?.insertBefore(this.canvas, target);
1134
1155
  target.style.visibility = "hidden";
1135
1156
  const ctx = this.canvas.getContext("2d");
1136
1157
  if (!ctx) throw new Error("video renderer: failed to acquire 2D context");
@@ -3056,5 +3077,5 @@ function defaultFallbackChain(strategy) {
3056
3077
  }
3057
3078
 
3058
3079
  export { UnifiedPlayer, avbridgeAudioToMediabunny, avbridgeVideoToMediabunny, buildMediabunnySourceFromInput, classifyContext, createPlayer, probe, srtToVtt };
3059
- //# sourceMappingURL=chunk-CUQD23WO.js.map
3060
- //# sourceMappingURL=chunk-CUQD23WO.js.map
3080
+ //# sourceMappingURL=chunk-3AUGRKPY.js.map
3081
+ //# sourceMappingURL=chunk-3AUGRKPY.js.map