avbridge 2.12.1 → 2.13.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 +101 -0
- package/README.md +33 -0
- package/dist/{chunk-UM6WCSGL.cjs → chunk-OFJYEITB.cjs} +356 -91
- package/dist/chunk-OFJYEITB.cjs.map +1 -0
- package/dist/{chunk-BN7BRTLY.js → chunk-VOC24LYF.js} +357 -92
- package/dist/chunk-VOC24LYF.js.map +1 -0
- package/dist/element-browser.js +354 -111
- package/dist/element-browser.js.map +1 -1
- package/dist/element.cjs +2 -2
- package/dist/element.js +1 -1
- package/dist/index.cjs +8 -8
- package/dist/index.js +1 -1
- package/dist/player.cjs +457 -135
- package/dist/player.cjs.map +1 -1
- package/dist/player.d.cts +35 -4
- package/dist/player.d.ts +35 -4
- package/dist/player.js +457 -135
- package/dist/player.js.map +1 -1
- package/package.json +1 -1
- package/src/element/avbridge-player.ts +136 -28
- package/src/strategies/fallback/audio-output.ts +164 -35
- package/src/strategies/fallback/decoder.ts +336 -58
- package/src/strategies/fallback/video-renderer.ts +176 -34
- package/src/strategies/hybrid/decoder.ts +22 -19
- package/src/strategies/remux/pipeline.ts +12 -3
- package/dist/chunk-BN7BRTLY.js.map +0 -1
- package/dist/chunk-UM6WCSGL.cjs.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,107 @@ 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.13.0]
|
|
8
|
+
|
|
9
|
+
Post-seek fast-forward fix on the fallback path, plus seekbar UX
|
|
10
|
+
improvements and a small new API for hosts embedding the player in
|
|
11
|
+
swipe-driven UIs.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **Post-seek "fast-forward" on MPEG-4 ASP + MP3 AVIs** (the headline
|
|
16
|
+
fix). After seeking deep into a long AVI, ~1–2 s of visibly
|
|
17
|
+
fast-forwarded video preceded normal playback. Root cause: the
|
|
18
|
+
fallback decoder's synthetic-PTS counter was reset to the user's
|
|
19
|
+
requested seek time, so the first NOPTS frame post-seek (typically
|
|
20
|
+
the keyframe libav landed on, ~4–8 s before target) got stamped
|
|
21
|
+
with the seek target's PTS. Every libav-pts frame after that was
|
|
22
|
+
REGRESSED-DROP'd because the synthetic anchor was ahead of real
|
|
23
|
+
content. The renderer painted the ~1-in-3 mis-labeled frames at
|
|
24
|
+
audio rate, producing 2.4–3× content rate on screen until the
|
|
25
|
+
synthetic counter caught up. Rewrote the content clock as
|
|
26
|
+
**sync-on-every-valid-pts / step-on-NOPTS** with no fallback to
|
|
27
|
+
seekTarget — labels now match real content within one frame at all
|
|
28
|
+
times. See `docs/dev/POSTMORTEMS.md` (2026-06-01) for the full
|
|
29
|
+
diagnostic arc, including the two epistemic traps that delayed
|
|
30
|
+
triage by hours.
|
|
31
|
+
- **Cold-start opening loses 1–2 frames.** First emitted keyframe at
|
|
32
|
+
`t=0` was being discarded as pre-anchor NOPTS, so the first paint
|
|
33
|
+
landed at content ~80 ms instead of 0. Special-cased
|
|
34
|
+
`seekTarget === 0`: anchor `lastContentUs = 0` on the
|
|
35
|
+
`f.key_frame === 1` frame directly. Strictly branched so it can't
|
|
36
|
+
touch the seek path.
|
|
37
|
+
- **Seekbar pointer events bubble to host gesture recognizers.**
|
|
38
|
+
Touches that start on the player chrome (seek bar, buttons,
|
|
39
|
+
settings menu, overlay play button) now stop propagation on
|
|
40
|
+
`pointerdown` / `pointermove` / `pointerup` / `pointercancel`, so
|
|
41
|
+
a host page that wraps the player in a TikTok-style vertical
|
|
42
|
+
pager won't latch its swipe handler on seek interactions.
|
|
43
|
+
- **Hybrid strategy audio uses packet PTS** instead of the synthetic
|
|
44
|
+
counter, matching the fallback strategy's audio path (see v2.12.2
|
|
45
|
+
refactor). `decodeAudioBatch` now captures `packetPtsSec` before
|
|
46
|
+
decode and routes per-frame; eliminates a class of post-seek
|
|
47
|
+
audio-scheduling drift that affected hybrid playback the same way
|
|
48
|
+
it affected fallback.
|
|
49
|
+
- **Remux pipeline drops stale writes after seek.** Each output's
|
|
50
|
+
`write` callback now captures the pump token at creation time and
|
|
51
|
+
drops any in-flight chunk whose token has been bumped by a
|
|
52
|
+
subsequent seek. Without this, fragments from the pre-seek output
|
|
53
|
+
could append to the SourceBuffer at their original timestamps,
|
|
54
|
+
the deferred seek would apply against the wrong buffered range,
|
|
55
|
+
and playback would snap to the end of the stale range.
|
|
56
|
+
|
|
57
|
+
### Added
|
|
58
|
+
|
|
59
|
+
- **`AvbridgePlayerElement.isPlayerChromeEvent(event)`** static
|
|
60
|
+
helper. Returns `true` if a DOM event originated from the player's
|
|
61
|
+
interactive chrome (works across the shadow boundary via
|
|
62
|
+
`composedPath()`). Use this in **capture-phase** gesture
|
|
63
|
+
recognizers — capture-phase listeners run before the player's own
|
|
64
|
+
handlers can stop propagation, so they need to check the path
|
|
65
|
+
themselves. See README → `<avbridge-player>` → "Embedding inside a
|
|
66
|
+
swipe gesture".
|
|
67
|
+
|
|
68
|
+
### Changed
|
|
69
|
+
|
|
70
|
+
- **Seekbar drag model** switches on `(pointer: coarse)` instead of
|
|
71
|
+
bar width. Touch devices (coarse) now get **relative-drag
|
|
72
|
+
scrubbing** — initial tap doesn't move the thumb, finger Δx maps to
|
|
73
|
+
Δt added to the time at pointerdown. Mouse/trackpad (fine) keep
|
|
74
|
+
**absolute-position** seeking — thumb jumps under the cursor on
|
|
75
|
+
tap, follows pointer on drag. Both modes commit live during drag
|
|
76
|
+
(throttled to ~4 Hz so the decoder pump isn't overwhelmed) and
|
|
77
|
+
once more on pointerup. Removes the 400 px `SCRUB_WIDTH_THRESHOLD`
|
|
78
|
+
— width was the wrong signal for distinguishing
|
|
79
|
+
precise-pointer-input vs imprecise-finger-input.
|
|
80
|
+
- **Diagnostic logging gated behind `globalThis.AVBRIDGE_DEBUG`.**
|
|
81
|
+
Per-packet (`[DIAG-PKT]`), per-frame (`[DIAG-FRAME]`), per-paint
|
|
82
|
+
(`[TRACE] PAINT`), and per-audio-chunk (`[TRACE-AUD]`) trace lines
|
|
83
|
+
are silent in normal use. Degraded-state warnings
|
|
84
|
+
(`first valid raw pts ≥ seek target`, `LEGACY (no PTS)`,
|
|
85
|
+
`REBASE anchor`) stay unconditional — those signal real problems.
|
|
86
|
+
- **Pre-roll re-enabled** with a structural safety guarantee. The
|
|
87
|
+
renderer paints at most ONE frame (the head of the queue, held
|
|
88
|
+
static) during the post-flush gate-wait window. Because the
|
|
89
|
+
decoder fix discards pre-target frames before they reach the
|
|
90
|
+
renderer queue, the pre-roll frame is now guaranteed to be a
|
|
91
|
+
near-target frame — the previous regression artifact (painting the
|
|
92
|
+
keyframe-to-target preroll stream) is structurally impossible.
|
|
93
|
+
|
|
94
|
+
### Internal
|
|
95
|
+
|
|
96
|
+
- Replaced the fallback decoder's `sanitizeFrameTimestamp` call-site
|
|
97
|
+
with an inline anchor-and-step block that never lies about content
|
|
98
|
+
position. Synthetic counter no longer initialized to anything
|
|
99
|
+
derived from the user's click — unanchored on seek, established at
|
|
100
|
+
the first valid libav pts, extended by `frameStep` on NOPTS,
|
|
101
|
+
re-synced to truth on every valid pts.
|
|
102
|
+
- Removed dead debug scaffolding from `video-renderer.ts`:
|
|
103
|
+
`BREAK_AT_PAINT_AFTER_FLUSH` constant, `paintsSinceFlush` counter,
|
|
104
|
+
unused `paintHistory` ring-buffer field, the `debugger` statement
|
|
105
|
+
that would trap users on paint #10 post-seek when
|
|
106
|
+
`AVBRIDGE_DEBUG=true`.
|
|
107
|
+
|
|
7
108
|
## [2.12.1]
|
|
8
109
|
|
|
9
110
|
DivX/Xvid AVI playback reliability — four latent bugs fixed. Content
|
package/README.md
CHANGED
|
@@ -431,6 +431,39 @@ attribute mirroring the controls auto-hide state — useful if slotted
|
|
|
431
431
|
buttons need to drive JS behavior (focus, announcements) in sync with
|
|
432
432
|
the fade, not just CSS opacity.
|
|
433
433
|
|
|
434
|
+
#### Embedding inside a swipe gesture (TikTok-style pagers, etc.)
|
|
435
|
+
|
|
436
|
+
When `<avbridge-player>` is nested inside a host UI that recognizes
|
|
437
|
+
swipe gestures (vertical pager, drawer, carousel), pointer events
|
|
438
|
+
that start on the player chrome — seek bar, buttons, settings menu,
|
|
439
|
+
overlay play button — should NOT also latch the host's gesture
|
|
440
|
+
recognizer. The player handles this in two layers:
|
|
441
|
+
|
|
442
|
+
**Bubble-phase listeners** (the default) need no action on your
|
|
443
|
+
side. The player calls `stopPropagation()` on `pointerdown`,
|
|
444
|
+
`pointermove`, `pointerup`, and `pointercancel` for chrome
|
|
445
|
+
interactions, so they never bubble out to the host.
|
|
446
|
+
|
|
447
|
+
**Capture-phase listeners** (`{ capture: true }`) run *before* the
|
|
448
|
+
player's handlers, so `stopPropagation()` can't help. Check the
|
|
449
|
+
event against the static helper instead:
|
|
450
|
+
|
|
451
|
+
```ts
|
|
452
|
+
import { AvbridgePlayerElement } from "avbridge/player-element";
|
|
453
|
+
|
|
454
|
+
document.addEventListener("pointerdown", (e) => {
|
|
455
|
+
if (AvbridgePlayerElement.isPlayerChromeEvent(e)) return;
|
|
456
|
+
startSwipeGesture(e);
|
|
457
|
+
}, { capture: true });
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
`isPlayerChromeEvent(event)` returns `true` only for events whose
|
|
461
|
+
`composedPath()` includes the player's interactive chrome (works
|
|
462
|
+
across the shadow boundary). Events on the bare video surface return
|
|
463
|
+
`false` — the host page remains free to claim those for its own
|
|
464
|
+
gestures (e.g. swipe-to-next-video). Events that didn't hit a player
|
|
465
|
+
at all return `false`.
|
|
466
|
+
|
|
434
467
|
This is a second tsup entry (`dist/element-browser.js`) that inlines
|
|
435
468
|
mediabunny + libavjs-webcodecs-bridge into a single ~1.3 MB file with
|
|
436
469
|
zero bare specifiers at runtime. Perfect for self-hosted tools or static
|