demuxe 0.3.0-beta.3
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/LICENSE +352 -0
- package/README.md +75 -0
- package/bin/demuxe.mjs +30 -0
- package/docs/API-MIGRATION.md +54 -0
- package/docs/BETA.md +98 -0
- package/docs/BRANDING-MIGRATION.md +77 -0
- package/docs/COMPATIBILITY-EXPANSION.md +181 -0
- package/docs/LICENSING.md +83 -0
- package/docs/OPTIMIZATION-COMPLETION.md +511 -0
- package/docs/OPTIMIZATION-FLAC.md +178 -0
- package/docs/OPTIMIZATION-INTEGRATION.md +221 -0
- package/docs/OPTIMIZATION-REVIEW-FIXES.md +78 -0
- package/docs/PLAYER-COMPONENT.md +247 -0
- package/docs/PUBLIC-API-VALIDATION.md +163 -0
- package/docs/PUBLIC-API.md +245 -0
- package/docs/RELEASE.md +181 -0
- package/docs/RUNTIME-ASSETS.md +127 -0
- package/engine-build.json +8781 -0
- package/examples/custom-controls.html +14 -0
- package/examples/player-element.html +5 -0
- package/fixtures/DejaVuSans.ttf +0 -0
- package/fixtures/FONT-LICENSE.txt +187 -0
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/package.json +46 -0
- package/player.d.ts +1 -0
- package/player.js +1 -0
- package/release-manifest.json +541 -0
- package/sources.lock.json +96 -0
- package/third_party/notices/dav1d/COPYING +23 -0
- package/third_party/notices/emscripten/LICENSE +102 -0
- package/third_party/notices/emscripten/system/lib/compiler-rt/LICENSE.TXT +311 -0
- package/third_party/notices/emscripten/system/lib/libc/musl/COPYRIGHT +193 -0
- package/third_party/notices/emscripten/system/lib/libcxx/LICENSE.TXT +311 -0
- package/third_party/notices/emscripten/system/lib/libcxxabi/LICENSE.TXT +311 -0
- package/third_party/notices/emsdk/LICENSE +27 -0
- package/third_party/notices/ffmpeg/COPYING.GPLv2 +339 -0
- package/third_party/notices/ffmpeg/COPYING.GPLv3 +674 -0
- package/third_party/notices/ffmpeg/COPYING.LGPLv2.1 +502 -0
- package/third_party/notices/ffmpeg/LICENSE.md +129 -0
- package/third_party/notices/ffmpeg-adaptation/COPYING.LGPLv2.1 +502 -0
- package/third_party/notices/ffmpeg-adaptation/LICENSE.md +127 -0
- package/third_party/notices/freetype/LICENSE.TXT +46 -0
- package/third_party/notices/freetype/docs/FTL.TXT +169 -0
- package/third_party/notices/freetype/docs/GPLv2.TXT +340 -0
- package/third_party/notices/fribidi/COPYING +504 -0
- package/third_party/notices/harfbuzz/COPYING +42 -0
- package/third_party/notices/libass/COPYING +15 -0
- package/third_party/notices/libplacebo/LICENSE +458 -0
- package/third_party/notices/libxml2/Copyright +23 -0
- package/third_party/notices/libxml2/dict.c.notice +17 -0
- package/third_party/notices/libxml2/list.c.notice +16 -0
- package/third_party/notices/mpv/Copyright +78 -0
- package/third_party/notices/mpv/LICENSE.GPL +338 -0
- package/third_party/notices/mpv/LICENSE.LGPL +501 -0
- package/third_party/notices/vulkan-headers/LICENSE.txt +202 -0
- package/third_party/notices/zimg/COPYING +14 -0
- package/third_party/notices/zlib/LICENSE +22 -0
- package/third_party/notices.json +166 -0
- package/toolchain.lock.json +204 -0
- package/web/audio-worklet.js +45 -0
- package/web/cheap-mp4-probe.js +82 -0
- package/web/engine-adaptation/manifest.json +75 -0
- package/web/engine-adaptation/remux.mjs +2 -0
- package/web/engine-adaptation/remux.wasm +0 -0
- package/web/engine-ass/manifest.json +55 -0
- package/web/engine-ass/subtitles.mjs +2 -0
- package/web/engine-ass/subtitles.wasm +0 -0
- package/web/engine-hybrid/player.mjs +2 -0
- package/web/engine-hybrid/player.wasm +0 -0
- package/web/engine-remux/remux.mjs +2 -0
- package/web/engine-remux/remux.wasm +0 -0
- package/web/engine-software-full/player.mjs +2 -0
- package/web/engine-software-full/player.wasm +0 -0
- package/web/file-reader.js +34 -0
- package/web/filter-retained-engine-worker.js +292 -0
- package/web/generated/index.d.ts +6 -0
- package/web/generated/index.js +4 -0
- package/web/generated/internal/assets.d.ts +2 -0
- package/web/generated/internal/assets.js +7 -0
- package/web/generated/internal/backend.d.ts +27 -0
- package/web/generated/internal/backend.js +1 -0
- package/web/generated/internal/errors.d.ts +13 -0
- package/web/generated/internal/errors.js +54 -0
- package/web/generated/internal/native-ass.d.ts +38 -0
- package/web/generated/internal/native-ass.js +206 -0
- package/web/generated/internal/native-player.d.ts +95 -0
- package/web/generated/internal/native-player.js +502 -0
- package/web/generated/internal/playback-plans.d.ts +526 -0
- package/web/generated/internal/playback-plans.js +110 -0
- package/web/generated/internal/selection.d.ts +45 -0
- package/web/generated/internal/selection.js +45 -0
- package/web/generated/internal/state.d.ts +8 -0
- package/web/generated/internal/state.js +56 -0
- package/web/generated/internal/wasm-player.d.ts +127 -0
- package/web/generated/internal/wasm-player.js +372 -0
- package/web/generated/player/index.d.ts +198 -0
- package/web/generated/player/index.js +873 -0
- package/web/generated/player/interaction.d.ts +6 -0
- package/web/generated/player/interaction.js +14 -0
- package/web/generated/player/styles.d.ts +1 -0
- package/web/generated/player/styles.js +86 -0
- package/web/generated/types.d.ts +227 -0
- package/web/generated/types.js +2 -0
- package/web/generated/unified-player.d.ts +129 -0
- package/web/generated/unified-player.js +1074 -0
- package/web/io-worker.js +65 -0
- package/web/native-ass-worker.js +26 -0
- package/web/native-remux-player.js +301 -0
- package/web/native-remux-source-worker.js +25 -0
- package/web/native-remux-worker.js +46 -0
- package/web/range-reader.js +130 -0
- package/web/remux-packaging.js +11 -0
- package/web/resource-loader.js +173 -0
- package/web/retained-decoder-worker.js +200 -0
- package/web/retained-video.js +15 -0
- package/web/segmented-subtitles.js +37 -0
- package/web/software-full-engine-worker.js +234 -0
- package/web/source-probe.js +28 -0
- package/web/split-mp4.js +159 -0
- package/web/streaming-manifest.js +136 -0
- package/web/subtitle-overlay.js +26 -0
- package/web/video-codec-config.js +59 -0
- package/web/vod-manifest.js +54 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# Optional player component
|
|
2
|
+
|
|
3
|
+
This is the target contract; qualification is recorded separately. Import
|
|
4
|
+
`definePlayerElement` from `demuxe/player` and call it once (repeat calls with the
|
|
5
|
+
same implementation are harmless). Core import does not import UI or register a
|
|
6
|
+
tag. SSR imports are safe; registration and construction require a browser.
|
|
7
|
+
|
|
8
|
+
`<demuxe-player controls asset-base="/assets/demuxe/" poster="/preview.jpg">`
|
|
9
|
+
creates one Player on connection. ready resolves with that core; player exposes it
|
|
10
|
+
read-only after initialization. open, close, destroy and playback conveniences
|
|
11
|
+
delegate to it. The component contains no route selector or playback scheduler.
|
|
12
|
+
|
|
13
|
+
src is a remote URL. Changing it cancels a previous pending source open; only the
|
|
14
|
+
latest accepted change wins. Removing src closes media. Programmatic open accepts
|
|
15
|
+
all core source types. autoplay requests play only after acceptance and reports
|
|
16
|
+
normal browser policy rejection without fallback. muted reflects configured mute;
|
|
17
|
+
controls toggles controls and closes open settings/stats overlays when disabled;
|
|
18
|
+
poster is an idle/loading preview. asset-base is fixed
|
|
19
|
+
after initialization and rejects changes. Set it before connection. Pre-upgrade
|
|
20
|
+
properties are replayed on upgrade. Boolean attributes follow HTML presence rules.
|
|
21
|
+
|
|
22
|
+
A microtask grace period preserves playback during synchronous DOM moves. Actual
|
|
23
|
+
removal aborts work and destroys the owned core; reconnect waits for that cleanup
|
|
24
|
+
before creating the next core. Explicit destroy is terminal, including reinsertion.
|
|
25
|
+
ready waits for connection; destroy before connection rejects it.
|
|
26
|
+
|
|
27
|
+
The open shadow root includes stage, controls, settings and status parts, CSS
|
|
28
|
+
public Demuxe theme variables, and before-controls /
|
|
29
|
+
after-controls slots. Labels can be overridden before or after connection.
|
|
30
|
+
Controls use semantic buttons/ranges/selects, visible focus, scoped keyboard
|
|
31
|
+
shortcuts, local drag preview, and an aria-live status that excludes time updates.
|
|
32
|
+
Settings restore focus on close. Fullscreen requests the component container in
|
|
33
|
+
the user gesture, keeping controls and subtitles together. PiP/casting are not
|
|
34
|
+
qualified. Loading, live windows and errors use core state only.
|
|
35
|
+
|
|
36
|
+
Forwarded core events are dispatched once with unchanged detail, bubbles:false;
|
|
37
|
+
listen directly on the element. No event-name aliases are generated. Component
|
|
38
|
+
lifecycle failures use error with a structured operation-scoped detail.
|
|
39
|
+
|
|
40
|
+
## Multiple files and queue
|
|
41
|
+
|
|
42
|
+
The component's media picker accepts multiple files. Selecting or dropping files
|
|
43
|
+
adds them in the supplied order, preserving duplicate filenames. If nothing is
|
|
44
|
+
queued, the first item opens; otherwise files append without interrupting playback.
|
|
45
|
+
The initial item follows `autoplay` (paused by default).
|
|
46
|
+
|
|
47
|
+
The folder menu shows the queue, with the current item highlighted. Select a file
|
|
48
|
+
to switch, remove individual items, or use **Clear queue** to stop and release all
|
|
49
|
+
queued sources. Removing the current item opens the next available item, or the
|
|
50
|
+
previous one if it was last. If that replacement fails, the removed media is
|
|
51
|
+
closed and the remaining queue stays available for retry or selection. Removing
|
|
52
|
+
other items leaves playback untouched.
|
|
53
|
+
|
|
54
|
+
Previous/next buttons and an item count appear beside the timeline controls when
|
|
55
|
+
there is more than one item. Manual changes preserve play/pause intent; an explicit
|
|
56
|
+
play or pause during loading overrides the intent captured when switching. Reaching
|
|
57
|
+
the end advances and plays the next file; the final item stops without looping.
|
|
58
|
+
An opening failure stops on that item and uses the existing error/retry UI;
|
|
59
|
+
it does not silently skip through the queue.
|
|
60
|
+
|
|
61
|
+
`element.open(source)`, `src` changes, and URL submissions replace the queue with
|
|
62
|
+
one source. `close()`, destruction and actual disconnection clear the queue and
|
|
63
|
+
release its file references. Synchronous DOM moves keep it. No queued item is
|
|
64
|
+
preloaded, and all opens delegate to the same core `Player`.
|
|
65
|
+
|
|
66
|
+
`showSourceControls = false` hides and disables queue management along with file
|
|
67
|
+
input. Previous/next playback controls still work. `allowFileDrop` independently
|
|
68
|
+
controls file drops. Files in a batch are treated as media; use the separate
|
|
69
|
+
subtitle picker to attach subtitles.
|
|
70
|
+
|
|
71
|
+
The queue is built-in UI state, not a second core playback API. Applications
|
|
72
|
+
needing their own playlist policies can use the core `Player` and manage sources
|
|
73
|
+
themselves.
|
|
74
|
+
|
|
75
|
+
## Customization
|
|
76
|
+
|
|
77
|
+
```html
|
|
78
|
+
<demuxe-player
|
|
79
|
+
controls
|
|
80
|
+
title="Movie Night"
|
|
81
|
+
asset-base="/assets/demuxe/">
|
|
82
|
+
</demuxe-player>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The standard HTML `title` property/attribute updates the displayed title live.
|
|
86
|
+
`titleMode` (also `title-mode`) has four policies:
|
|
87
|
+
|
|
88
|
+
* `auto` (default): a nonempty custom `title`, otherwise the accepted source
|
|
89
|
+
filename, otherwise nothing.
|
|
90
|
+
* `custom`: only `title`; an empty string hides it.
|
|
91
|
+
* `source`: only the accepted source filename, even when a custom title exists.
|
|
92
|
+
* `none`: hide the title.
|
|
93
|
+
|
|
94
|
+
Local files use `File.name`. HTTP(S)/file URLs, including remote source
|
|
95
|
+
descriptors, use the decoded final pathname filename only. Origins, credentials,
|
|
96
|
+
query strings and fragments are excluded. Directory URLs, ArrayBuffers, opaque
|
|
97
|
+
URLs such as blob/data URLs, and unnamed sources show nothing. Source titles
|
|
98
|
+
track successful `element.open()` / `src` opens and clear when that source closes
|
|
99
|
+
or is replaced through the core; applications opening directly through
|
|
100
|
+
`element.player` should manage a custom title. Titles are plain text and truncate
|
|
101
|
+
visually to fit the player.
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
const element = document.querySelector("demuxe-player");
|
|
105
|
+
element.titleMode = "source";
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
For an embedded app, disable utility UI while retaining playback controls:
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
element.showSourceControls = false;
|
|
112
|
+
element.showDiagnostics = false;
|
|
113
|
+
element.allowFileDrop = false;
|
|
114
|
+
await element.open(source); // The host application owns source selection.
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
These three properties default to `true` and update live. Source controls include
|
|
118
|
+
the empty-state opener, folder menu, URL form and local media/subtitle pickers.
|
|
119
|
+
Disabling them closes their menu and disables the input handlers. Programmatic
|
|
120
|
+
`open()` and `addSubtitle()`, plus existing subtitle track selection, still work.
|
|
121
|
+
Disabling diagnostics closes its overlay; `element.player.diagnostics` remains
|
|
122
|
+
available. Disabling file drop leaves browser drag/drop defaults alone. Drop
|
|
123
|
+
handling is independent of source-control visibility: disable both when the host
|
|
124
|
+
owns all source input. Focus moves to the stage if a focused utility is disabled.
|
|
125
|
+
These options are JavaScript properties, not HTML boolean attributes.
|
|
126
|
+
|
|
127
|
+
`seekStep` defaults to 10 seconds and accepts a positive finite number. It
|
|
128
|
+
controls both seek buttons, their default accessible labels/numerals, and J/L.
|
|
129
|
+
Arrow keys retain their five-second steps. Explicit localized `labels.back` and
|
|
130
|
+
`labels.forward` override the default labels; keep them consistent with your step.
|
|
131
|
+
`controlsAutoHideDelay` defaults to 2800 milliseconds; zero disables inactivity
|
|
132
|
+
hiding, and positive values up to 2147483647 set the delay. Changing it restarts
|
|
133
|
+
the timer. Playing with the play button/shortcut still hides controls immediately;
|
|
134
|
+
screen taps still toggle visibility. Invalid numeric property values throw
|
|
135
|
+
`INVALID_ARGUMENT`.
|
|
136
|
+
|
|
137
|
+
For entirely custom controls, import `Player` from `demuxe` and build your own
|
|
138
|
+
layout. The optional component delegates to that same core and adds no playback
|
|
139
|
+
modes or routing policy.
|
|
140
|
+
|
|
141
|
+
```js
|
|
142
|
+
import {definePlayerElement} from 'demuxe/player';
|
|
143
|
+
definePlayerElement();
|
|
144
|
+
const element = document.querySelector('demuxe-player');
|
|
145
|
+
element.labels = {play: 'Lire', pause: 'Pause', settings: 'Réglages'};
|
|
146
|
+
const core = await element.ready;
|
|
147
|
+
const unsubscribe = core.subscribe(state => console.log(state.status));
|
|
148
|
+
await element.open(file, {signal: controller.signal});
|
|
149
|
+
// Later: await element.close() to reuse, or await element.destroy() to finish.
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Methods also include play, pause, seek, setVolume, setMuted, setPlaybackRate,
|
|
153
|
+
selectAudioTrack, selectSubtitleTrack and addSubtitle. Advanced font, filter,
|
|
154
|
+
tone-mapping and source policy methods are available through the read-only player
|
|
155
|
+
reference. Replacing that reference is unsupported. Use a new element for a
|
|
156
|
+
new asset-base; the old one must finish cleanup first. An asset-base attribute
|
|
157
|
+
change after initialization is reverted and reports INVALID_ARGUMENT.
|
|
158
|
+
|
|
159
|
+
```css
|
|
160
|
+
demuxe-player {
|
|
161
|
+
--demuxe-background: #121318;
|
|
162
|
+
--demuxe-stage-background: #090a10;
|
|
163
|
+
--demuxe-foreground: #f2f1f7;
|
|
164
|
+
--demuxe-muted-foreground: #bbb8ca;
|
|
165
|
+
--demuxe-panel-background: #20212a;
|
|
166
|
+
--demuxe-control-background: #30313e;
|
|
167
|
+
--demuxe-overlay-background: #171824bb;
|
|
168
|
+
--demuxe-accent: #b7a0ff;
|
|
169
|
+
--demuxe-border: #393941;
|
|
170
|
+
--demuxe-radius: 16px;
|
|
171
|
+
}
|
|
172
|
+
demuxe-player::part(controls) { padding-inline: 20px; }
|
|
173
|
+
demuxe-player::part(title) { font-weight: 600; }
|
|
174
|
+
demuxe-player::part(timeline) { height: 32px; }
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
For a light skin, set the same surface variables to light colors, foregrounds to
|
|
178
|
+
dark colors, and `color-scheme: light` on the element for native form controls.
|
|
179
|
+
All public theme names use `--demuxe-*`; there are no legacy branding aliases.
|
|
180
|
+
|
|
181
|
+
Stable parts: container, stage, controls, settings, error, status, title, topbar,
|
|
182
|
+
transport, timeline, volume. The volume part wraps the mute button and slider. Limited slots:
|
|
183
|
+
before-controls, after-controls and source-actions. The source-actions slot adds
|
|
184
|
+
host-owned buttons to the source menu; activating a button closes the menu and
|
|
185
|
+
focuses the stage. The playground uses it for its example media. Shadow IDs/classes are implementation details.
|
|
186
|
+
Air controls overlay the video: an open-media action at the top, a large unboxed
|
|
187
|
+
play/pause icon between backward/forward seek buttons (ten seconds by default) at the center,
|
|
188
|
+
and a thin full-width timeline with elapsed and total times at opposite ends.
|
|
189
|
+
Seek buttons clamp to available seek ranges and disable while an operation is
|
|
190
|
+
pending or no seek window exists. Volume shares the timestamp row below the
|
|
191
|
+
timeline; settings and fullscreen sit at the top right. The gear opens compact playback settings; the folder opens file/URL
|
|
192
|
+
inputs and subtitle upload separately. The eye toggles a live session diagnostics
|
|
193
|
+
text overlay that remains visible when playback controls hide. The overlay is
|
|
194
|
+
scrollable and keyboard-focusable; navigation keys scroll it, and Escape closes
|
|
195
|
+
it and returns focus to the eye. Top-right icons
|
|
196
|
+
use motion on hover and a stronger accent stroke when active. Buffering displays
|
|
197
|
+
a small central ring even with hidden controls. Timeline shading uses reported
|
|
198
|
+
buffered ranges (currently Native only); unavailable ranges are not estimated.
|
|
199
|
+
Control sizes and compact layouts follow the player container width, including
|
|
200
|
+
small embeds in wide browser windows. Pointer controls retain 36px targets and
|
|
201
|
+
coarse-pointer devices use 44px targets. On narrow players, opening a menu hides
|
|
202
|
+
the central controls to avoid overlap. Play hides controls immediately; mouse
|
|
203
|
+
movement reveals them, and inactivity fades them out. Leaving the player with
|
|
204
|
+
the mouse also hides controls while playing (not while paused), unless a menu,
|
|
205
|
+
pending operation, timeline drag, or keyboard-visible focus needs them. Losing
|
|
206
|
+
focus alone does not hide controls; touch input keeps its existing tap behavior. Seeking with hidden
|
|
207
|
+
controls briefly shows only the timeline and times, for 800 ms after completion. Tapping the video toggles controls
|
|
208
|
+
in playing or paused playback. Clicking outside a menu dismisses it without
|
|
209
|
+
stealing focus; Escape returns focus to its trigger. Hiding controls preserves
|
|
210
|
+
focus on the stage; keyboard navigation reveals controls. Keyboard shortcuts apply to
|
|
211
|
+
focus inside the component; inputs keep their native keys, and buttons retain
|
|
212
|
+
Space/Enter activation, except top-right icons reserve Space for playback
|
|
213
|
+
and use Enter for activation. Playback keys: Space/K, arrows, J/L, M, C, brackets, digits,
|
|
214
|
+
Home/End, F and ?. Inputs, selects, editable content and modified keys
|
|
215
|
+
retain their normal handling. C uses the core's subtitle visibility setting;
|
|
216
|
+
it does not select a different language. Scrubbing previews locally; release
|
|
217
|
+
commits one seek. Settings are an accessible disclosure and restore trigger focus.
|
|
218
|
+
|
|
219
|
+
Local file/subtitle pickers are available in settings; opening files never uploads
|
|
220
|
+
them. File drop and a URL form (File, HLS, DASH, and live input)
|
|
221
|
+
are also built into the component. File selection, URL submission, and file drop
|
|
222
|
+
focus the stage so Space controls playback; the public close() method closes media. The component contains no example media, engine selector,
|
|
223
|
+
raw filters or memory metrics. The playground keeps automatic engine selection
|
|
224
|
+
enabled and shows a compact engine/media summary below the player. A live stream with no known seek window shows LIVE
|
|
225
|
+
and disables the finite seek control. Browser fullscreen denial produces a message;
|
|
226
|
+
no fake fullscreen, PiP or casting fallback is applied.
|
|
227
|
+
|
|
228
|
+
Experimental playback optimizations use the existing Player owner and do not add
|
|
229
|
+
public modes or automatic component controls. See [the integration stage](OPTIMIZATION-INTEGRATION.md)
|
|
230
|
+
for constructor flags, scalar gain, and current qualification limits.
|
|
231
|
+
|
|
232
|
+
The programmatic Player option `experimentalAudioAdaptation: 'flac'` is an explicit
|
|
233
|
+
Native trial gate. No new component attribute or automatic route is introduced.
|
|
234
|
+
See [the FLAC qualification report](OPTIMIZATION-FLAC.md) for required optional assets,
|
|
235
|
+
track/fidelity limits and unsupported subtitle combinations.
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
## Optional playback processing
|
|
239
|
+
|
|
240
|
+
The component delegates playback, subtitle selection and visibility to its core
|
|
241
|
+
Player; it owns no separate optimization route or audio graph. Applications using
|
|
242
|
+
an explicitly configured core can call `setAudioGain` repeatedly without replacing
|
|
243
|
+
the video session. Ordinary component volume/mute controls retain their existing
|
|
244
|
+
meaning. The component fullscreen container includes subtitle canvases. Native
|
|
245
|
+
ASS and adaptation require explicit core constructor options and optional assets;
|
|
246
|
+
there are no new component attributes or silently enabled experimental defaults.
|
|
247
|
+
See [core options and restrictions](PUBLIC-API.md#experimental-playback-optimizations).
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# Public API and component qualification
|
|
2
|
+
|
|
3
|
+
Implementation baseline: working files matched demo-source 8bb451b while main was
|
|
4
|
+
f9a8f9c. No branch was merged or published. The pre-change source snapshot is
|
|
5
|
+
build/public-api-baseline/source-before.tar.gz. Historical results and failed
|
|
6
|
+
artifacts are retained.
|
|
7
|
+
|
|
8
|
+
## Milestone status
|
|
9
|
+
|
|
10
|
+
| Milestone | Implementation | Qualification boundary |
|
|
11
|
+
| --- | --- | --- |
|
|
12
|
+
| M0 | Public contract, component contract, migration notes | Source-reviewed against current transaction/engine owners |
|
|
13
|
+
| M1 | Immutable snapshots, subscriptions, typed events, media geometry/tracks, capabilities | Real Native/Hybrid/Software tests; unknown/live-window mappings have separate adapter-injection evidence |
|
|
14
|
+
| M2 | Additive commands, stable IDs, cancellation, close/reopen, idempotent teardown, structured/redacted errors | Real rollback, stale-event, mute, track transition, seek and cleanup checks |
|
|
15
|
+
| M3 | Same-origin runtime base, verified copy CLI, static and bundled entry points, SSR-safe imports | Exact archives installed into independent static and esbuild apps at two runtime paths |
|
|
16
|
+
| M4 | Optional custom element and migrated playground, same core | Two independent instances, source changes, removal/reconnect and actual fullscreen |
|
|
17
|
+
| M5 | Keyboard, semantic controls, accessible names, focus restoration, live announcements, styling hooks | Chrome/Firefox keyboard and accessibility-tree checks; manual screen-reader and physical touch sessions remain open |
|
|
18
|
+
| M6 | Tests, examples, package evidence, overhead samples | Local candidate qualification only; clean three-engine builds, Safari/mobile, physical output and release gates remain open |
|
|
19
|
+
|
|
20
|
+
Archive-specific qualification is generated **after** assembly, alongside the
|
|
21
|
+
archive as qualification.json. An embedded copy of this document describes the
|
|
22
|
+
build-time status; it cannot contain its own archive hash. Consult the accompanying
|
|
23
|
+
record and the repository results for final pass/fail status and test hashes.
|
|
24
|
+
|
|
25
|
+
## Reproducible commands
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
npm run build
|
|
29
|
+
node --test tests/public-api-state.mjs
|
|
30
|
+
node tests/public-api.mjs
|
|
31
|
+
BROWSER=firefox node tests/public-api.mjs
|
|
32
|
+
node tests/player-component.mjs
|
|
33
|
+
BROWSER=firefox node tests/player-component.mjs
|
|
34
|
+
node tests/autoplay-policy.mjs
|
|
35
|
+
BROWSER=firefox node tests/autoplay-policy.mjs
|
|
36
|
+
node tests/player-api.mjs
|
|
37
|
+
node scripts/media-server.mjs # separate terminal; synthetic origins 4180/4181
|
|
38
|
+
node --test tests/range-reader-deadline.mjs tests/range-reader.mjs \
|
|
39
|
+
tests/audio-worklet.mjs tests/resource-loader.mjs tests/streaming-expansion.mjs \
|
|
40
|
+
tests/remux-buffering.mjs tests/video-codec-config.mjs \
|
|
41
|
+
tests/retained-codec-worker.mjs tests/native-selection.mjs
|
|
42
|
+
ONLY='Live,External,HLS,Finite DASH,Audio device' node tests/compatibility-expansion.mjs
|
|
43
|
+
node tests/player-ui-overhead.mjs
|
|
44
|
+
npm install --prefix build/public-api-tooling --no-audit --no-fund esbuild@0.28.2
|
|
45
|
+
python3 scripts/package-beta.py --output build/public-api-release-candidate
|
|
46
|
+
BETA_ARCHIVE=build/public-api-release-candidate/demuxe-0.3.0-beta.3.tgz node --test tests/copy-assets.mjs
|
|
47
|
+
BETA_ARCHIVE=build/public-api-release-candidate/demuxe-0.3.0-beta.3.tgz node tests/public-api-consumer.mjs
|
|
48
|
+
BETA_ARCHIVE=build/public-api-release-candidate/demuxe-0.3.0-beta.3.tgz BROWSER=firefox node tests/public-api-consumer.mjs
|
|
49
|
+
BETA_ARCHIVE=build/public-api-release-candidate/demuxe-0.3.0-beta.3.tgz node tests/beta-streaming.mjs
|
|
50
|
+
BETA_ARCHIVE=build/public-api-release-candidate/demuxe-0.3.0-beta.3.tgz BROWSER=firefox node tests/beta-streaming.mjs
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Results go to timestamped results/public-api/, results/player-component/,
|
|
54
|
+
results/public-api-consumer/, results/beta/ and existing regression directories.
|
|
55
|
+
The archive tests record SHA-256, browser version, consumer root and case results.
|
|
56
|
+
The esbuild application imports only shipped package entry points and copies its
|
|
57
|
+
runtime via the shipped command. No repository-specific browser code is needed.
|
|
58
|
+
|
|
59
|
+
## Interpreting evidence
|
|
60
|
+
|
|
61
|
+
The prior 21-case API suite, 73 reader/routing/audio unit checks and focused HLS,
|
|
62
|
+
DASH, subtitle/font and audio-device regressions passed during this work. New
|
|
63
|
+
normalized-state and component suites exercise real engines, while explicitly
|
|
64
|
+
labeled adapter injection tests cover unknown metadata and live-window mapping.
|
|
65
|
+
They do not certify every live demuxer or codec. Browser isolation and missing
|
|
66
|
+
assets are tested as distinct errors. Missing assets must be rejected at the HTTP
|
|
67
|
+
origin because worker fetch interception differs across automation engines.
|
|
68
|
+
|
|
69
|
+
Autoplay checks must not run evaluation polling while the initial play request
|
|
70
|
+
is pending: automation evaluation can grant transient activation. Chrome rejected
|
|
71
|
+
Native and Hybrid and resumed after a click. Firefox rejected Native but allowed
|
|
72
|
+
the Web Audio Hybrid path in the observed environment; that difference is not
|
|
73
|
+
reported as a codec failure or an invented autoplay rejection.
|
|
74
|
+
|
|
75
|
+
The UI adds no engine polling. The short overhead harness compares the same
|
|
76
|
+
example.mp4, mode and 1920×1080 output for four seconds, using browser main-thread
|
|
77
|
+
ScriptDuration. Those samples describe UI/event work only, not full engine CPU,
|
|
78
|
+
real-time output quality, sustained playback or a performance improvement.
|
|
79
|
+
|
|
80
|
+
The in-app browser plugin could not connect because an installed module was
|
|
81
|
+
missing. Qualification used standalone real Chrome/Firefox automation. No manual
|
|
82
|
+
VoiceOver/NVDA session, physical touch, Safari, PiP, casting, physical HDR/surround
|
|
83
|
+
fidelity or independent clean build of all three engines is claimed. GPL/source
|
|
84
|
+
obligations and the release gate in RELEASE.md remain intact. The public Pages
|
|
85
|
+
site has not been updated by this work.
|
|
86
|
+
|
|
87
|
+
## Archive-specific record
|
|
88
|
+
|
|
89
|
+
The final archive hash and exact result paths are recorded after assembly in the
|
|
90
|
+
accompanying qualification.json and in the working repository's final report.
|
|
91
|
+
This embedded build-time statement deliberately contains no self-referential
|
|
92
|
+
archive hash. Earlier candidates, failures and their evidence remain preserved.
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
## Final local candidate — 2026-09-12
|
|
96
|
+
|
|
97
|
+
Artifact: `build/public-api-release-candidate/demuxe-0.3.0-beta.3.tgz` (17,845,779 bytes).
|
|
98
|
+
|
|
99
|
+
SHA-256: `2b7d99731bb05c33d8d24648550fafda90c4ad13968d2dbbdb777ef48421bb4a`.
|
|
100
|
+
|
|
101
|
+
The package assembled alongside `build/pages-api-release-candidate` is byte-identical.
|
|
102
|
+
Every packaged manifest entry was verified. Generated core/component files match the
|
|
103
|
+
working implementation; all three shipped Wasm binaries match the previous Pages
|
|
104
|
+
manifest. These are existing engine binaries, not newly qualified clean engine builds.
|
|
105
|
+
The final report is external to the archive to avoid a self-referential archive hash.
|
|
106
|
+
|
|
107
|
+
| Suite | Chrome 152.0.7977.83 | Firefox 146.0.1 | Evidence scope |
|
|
108
|
+
| --- | --- | --- | --- |
|
|
109
|
+
| Normalized core | 14/14 | 14/14 | Current core source hashes |
|
|
110
|
+
| Component | 14/14 | 14/14 | Current component source hashes, including release of retry sources |
|
|
111
|
+
| Clean static and esbuild consumers | 8/8 | 8/8 | Exact archive; two runtime bases, all three modes, SSR/types/import isolation |
|
|
112
|
+
| Streaming/seek | 6/6 | 6/6 | Exact archive; packet integrity, absolute deadline, cleanup |
|
|
113
|
+
| Local Pages assembly | 8/8 | 8/8 | Matching packaged runtime manifest; no public deployment |
|
|
114
|
+
| Autoplay policy | 2/2 | 2/2 | Recorded browser policy behavior, including Firefox allowing Hybrid |
|
|
115
|
+
|
|
116
|
+
Additional checks: 4 asset-copy tests, 5 pure state/error tests, 73 existing unit
|
|
117
|
+
checks, 21 existing API cases and 7 focused compatibility cases passed. The actual
|
|
118
|
+
`npx --offline demuxe copy-assets npx-runtime` command copied 96 verified assets
|
|
119
|
+
from a clean installation of this archive. Exact paths, result hashes, CLI output,
|
|
120
|
+
source/build-material archive hashes and qualification boundaries are recorded in
|
|
121
|
+
`build/public-api-release-candidate/qualification.json`.
|
|
122
|
+
|
|
123
|
+
A Pages harness failure is retained at
|
|
124
|
+
`results/pages/firefox-2026-09-12T14-06-43.067Z/result.json`.
|
|
125
|
+
The harness could mistake the previous source's idle operation state for completion
|
|
126
|
+
of the asynchronously fetched example. The final harness explicitly waits for a
|
|
127
|
+
new accepted source identity. This test-only correction does not change package
|
|
128
|
+
bytes. Historical candidates and failures remain preserved.
|
|
129
|
+
|
|
130
|
+
Four-second matched playback samples measured core/component main-thread script
|
|
131
|
+
work of 4.027/6.377 ms (Native), 44.603/57.464 ms (Hybrid), and 19.553/32.696 ms
|
|
132
|
+
(Software). Notification counts were equal within each pair: 19, 129 and 129.
|
|
133
|
+
These earlier short samples are not engine CPU or sustained performance claims.
|
|
134
|
+
|
|
135
|
+
The implementation is complete for the stated scope. M5's manual screen-reader
|
|
136
|
+
and physical touch gate and M6's broader browser/clean-engine/release gates remain
|
|
137
|
+
open as listed above. No commit, push, merge or publication was performed.
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
## Review corrections — 2026-09-12
|
|
141
|
+
|
|
142
|
+
The candidate above is historical and predates three review corrections: external
|
|
143
|
+
subtitle identities no longer alias their per-file stream indices; unrecovered
|
|
144
|
+
fatal mpv end-file events populate the structured session error before notification;
|
|
145
|
+
and caller cancellation is detached at source acceptance, before subscribers run.
|
|
146
|
+
Instance close/destroy retain their internal cancellation authority.
|
|
147
|
+
|
|
148
|
+
`tests/public-api.mjs` now includes real two-attachment selection and cross-mode
|
|
149
|
+
retention, fatal-event adapter injection for each public mode, and synchronous
|
|
150
|
+
abort-at-sourcechange checks in Native and Software. The existing pre-acceptance
|
|
151
|
+
cancellation, rollback, stale-event and bounded-close checks remain in that suite.
|
|
152
|
+
`tests/public-api-state.mjs` additionally checks external/embedded ID separation.
|
|
153
|
+
New timestamped results and a separate review-fix archive preserve the previous
|
|
154
|
+
candidate and its evidence. The review does not change the release gates above.
|
|
155
|
+
|
|
156
|
+
Review-fix validation: Chrome 152.0.7977.83 and Firefox 146.0.1 each passed
|
|
157
|
+
20 normalized API cases and 8 clean static/bundled consumer cases. The 6 state
|
|
158
|
+
unit tests and TypeScript build passed. The archive is
|
|
159
|
+
`build/public-api-review-fixes/demuxe-0.3.0-beta.3.tgz`, SHA-256
|
|
160
|
+
`806b07a4c7fc7df8c96781cc69b0f0fa77984f062ec0a51bf8ab0caef700bc42`. Its manifest hashes and generated runtime
|
|
161
|
+
match the working build; the Wasm engines are unchanged. Detailed result hashes
|
|
162
|
+
are in the accompanying `qualification.json`. Broader historical suites were
|
|
163
|
+
not rerun for these targeted core corrections.
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# Public API contract
|
|
2
|
+
|
|
3
|
+
Contract for the public API milestone work. Implementation and qualification are
|
|
4
|
+
tracked separately in PUBLIC-API-VALIDATION.md; this document is not test evidence.
|
|
5
|
+
The baseline is demo-source 8bb451b, present in the working tree on main f9a8f9c.
|
|
6
|
+
|
|
7
|
+
Player is the sole playback owner. It owns its video/canvas surface, bounded
|
|
8
|
+
source adapters, routing, synchronization, recovery and teardown. There are
|
|
9
|
+
exactly three modes: native, hybrid, software. UI and core imports are separate.
|
|
10
|
+
No arbitrary mpv command interface is exposed.
|
|
11
|
+
|
|
12
|
+
## Compatibility and additions
|
|
13
|
+
|
|
14
|
+
Existing: ready, open(File | ArrayBuffer), openRemote, play, pause, seek,
|
|
15
|
+
volume(0–100), rate(0.5–2), selectTrack with backend IDs, subtitleVisible,
|
|
16
|
+
addSubtitle, addTextTrack, addFont, setMode, setAutomaticSelection, filter and
|
|
17
|
+
tone-mapping methods, resize, destroy, properties, diagnostics, mpv events,
|
|
18
|
+
modechange and selectionchange.
|
|
19
|
+
|
|
20
|
+
Additions: state, subscribe, mediaInfo, typed high-level events, feature
|
|
21
|
+
availability, open(string | URL | RemoteSource), AbortSignal open options,
|
|
22
|
+
setVolume(0–1), setMuted, setPlaybackRate, stable public track selections, close,
|
|
23
|
+
assetBase. These are additive; legacy volume and rate units do not change.
|
|
24
|
+
|
|
25
|
+
## State and events
|
|
26
|
+
|
|
27
|
+
state is a deeply frozen snapshot. Its identity remains unchanged until a value
|
|
28
|
+
changes. subscribe(listener) calls the listener immediately and returns an
|
|
29
|
+
idempotent unsubscribe. Updates happen before subscribers and high-level events.
|
|
30
|
+
Ordinary backend property bursts are coalesced in a microtask, with no new worker
|
|
31
|
+
polling. Subscribers must not throw; observer failures do not change playback.
|
|
32
|
+
|
|
33
|
+
status describes observed idle/paused/playing/buffering/ended/error state;
|
|
34
|
+
playbackIntent describes requested play/pause independently. pendingOperation has
|
|
35
|
+
an increasing instance-local ID and opening/seeking/switching/closing kind.
|
|
36
|
+
activeMode is null without accepted media. Legacy mode remains the configured
|
|
37
|
+
route while idle. Candidate events never replace accepted state. Failed
|
|
38
|
+
replacement reports an operation error and preserves a working session.
|
|
39
|
+
|
|
40
|
+
Seconds are used throughout. Unknown duration and ranges are null; [] means
|
|
41
|
+
known empty. Byte-cache statistics are never converted into buffered time.
|
|
42
|
+
Native ranges use the browser timeline translated out of remux bias. mpv
|
|
43
|
+
buffered time is unknown; seek ranges use observed demuxer seekable ranges for
|
|
44
|
+
live sources, and confirmed demuxer seekability plus duration for VOD. An unknown
|
|
45
|
+
live window disables a finite seek slider. mediaInfo includes display geometry,
|
|
46
|
+
source rotation when reported, display aspect and selected streams; absent
|
|
47
|
+
metadata stays null. No UI needs mpv property names.
|
|
48
|
+
|
|
49
|
+
High-level event detail contains the committed state. Source acceptance emits
|
|
50
|
+
sourcechange; subsequent changed fields emit durationchange, trackschange,
|
|
51
|
+
capabilitieschange, volumechange, ratechange and timeupdate. play expresses
|
|
52
|
+
accepted play intent; playing requires observed playback progress, waiting
|
|
53
|
+
observed starvation, pause observed pause, ended EOF. seeking precedes the
|
|
54
|
+
backend seek; seeked occurs only after native seeked or mpv presentation settles.
|
|
55
|
+
statechange follows snapshot publication. modechange retains loading/ready/failed
|
|
56
|
+
candidate phases and selectionchange retains route-attempt detail.
|
|
57
|
+
|
|
58
|
+
## Completion, cancellation and ownership
|
|
59
|
+
|
|
60
|
+
open resolves after an accepted paused session loads and its initial output is
|
|
61
|
+
ready. File references are retained; ArrayBuffers are copied and limited to
|
|
62
|
+
32 MiB. Remote options are copied. open accepts {signal}; abort before acceptance
|
|
63
|
+
cancels inspection/candidate work and preserves the previous usable session.
|
|
64
|
+
Abort after acceptance has no effect. Caller-owned text-track URLs remain alive
|
|
65
|
+
until close/source replacement. Fonts have existing bounded instance budgets.
|
|
66
|
+
|
|
67
|
+
play resolves when the backend accepts playback, not at the first time update.
|
|
68
|
+
Call from a user gesture; resume is initiated in that gesture when no operation
|
|
69
|
+
is pending. Autoplay denial is AUTOPLAY_BLOCKED and never triggers codec fallback.
|
|
70
|
+
pause resolves at observed pause. seek resolves after output settles; it does not
|
|
71
|
+
interrupt active packet reads. There are at most 32 pending core operations.
|
|
72
|
+
Controls preview scrubbing locally and submit a seek on release.
|
|
73
|
+
|
|
74
|
+
close aborts older queued/in-flight operations, disposes media-specific resources
|
|
75
|
+
and attachments, and leaves a reusable idle instance. It retains configured
|
|
76
|
+
volume/mute/rate, mode policy, filters, tone mapping, output policy and bounded
|
|
77
|
+
fonts. destroy is terminal and idempotent, cancels work, disposes all instance
|
|
78
|
+
resources and removes owned DOM. Pending promises reject predictably. Await
|
|
79
|
+
cleanup before discarding the owner.
|
|
80
|
+
|
|
81
|
+
Stable track IDs are scoped to a source generation and mapped to backend IDs.
|
|
82
|
+
Source stream indices preserve identity across routes where reported. External
|
|
83
|
+
subtitle files use a separate attachment namespace: their demuxers can share the
|
|
84
|
+
same stream index. Replaying the retained attachments preserves their track IDs. Identity
|
|
85
|
+
that exists only within a backend cannot be guessed across Native/mpv routes;
|
|
86
|
+
explicit selection causes an unsupported-transition rejection. New source IDs
|
|
87
|
+
invalidate previous IDs. null means disabled; 'auto' delegates initial selection.
|
|
88
|
+
Legacy selectTrack retains its documented backend-ID contract.
|
|
89
|
+
|
|
90
|
+
Errors are PlayerError with code, operationId, operation, scope, retryable and
|
|
91
|
+
redacted message. Session errors alone populate state.error. Failed controls or
|
|
92
|
+
replacement errors are operation-scoped. Codes: INVALID_ARGUMENT, ABORTED,
|
|
93
|
+
AUTOPLAY_BLOCKED, SOURCE_PERMISSION, SOURCE_CHANGED, NETWORK_TIMEOUT,
|
|
94
|
+
UNSUPPORTED_MEDIA, UNSUPPORTED_TIMELINE, UNSUPPORTED_FEATURE, ASSET_LOAD_FAILED, ISOLATION_REQUIRED,
|
|
95
|
+
DECODE_FAILED. Normal events/diagnostics redact authorization fields, URL userinfo,
|
|
96
|
+
queries and fragments; raw mpv properties remain an advanced inspection surface.
|
|
97
|
+
|
|
98
|
+
## Using normalized state
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
const player = new Player(host, {assetBase: '/assets/demuxe/'});
|
|
102
|
+
const unsubscribe = player.subscribe(state => {
|
|
103
|
+
render({
|
|
104
|
+
status: state.status,
|
|
105
|
+
seconds: state.currentTime,
|
|
106
|
+
duration: state.duration,
|
|
107
|
+
seekWindow: state.seekable,
|
|
108
|
+
audio: state.audioTracks,
|
|
109
|
+
subtitles: state.subtitleTracks,
|
|
110
|
+
aspect: state.mediaInfo.aspectRatio,
|
|
111
|
+
seek: state.capabilities.features.seek,
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
await player.open(file, {signal});
|
|
115
|
+
await player.selectSubtitleTrack(player.state.subtitleTracks[0]?.id ?? null);
|
|
116
|
+
await player.setVolume(.75);
|
|
117
|
+
await player.setMuted(true);
|
|
118
|
+
await player.close();
|
|
119
|
+
unsubscribe();
|
|
120
|
+
await player.destroy();
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
state.volume is normalized 0–1, configured independently from mute. Legacy
|
|
124
|
+
properties.volume still reflects backend 0–100 output, including zero while
|
|
125
|
+
muted. subtitlesVisible exposes the retained visibility setting independently
|
|
126
|
+
from the selected subtitle identity. player.capabilities includes legacy booleans,
|
|
127
|
+
deployment flags and feature availability; the same object is in the snapshot.
|
|
128
|
+
Feature availability describes request/route eligibility, not a guarantee that
|
|
129
|
+
an untested codec, asset or output device will work. Source-specific timeline and
|
|
130
|
+
track availability remain unknown until established.
|
|
131
|
+
|
|
132
|
+
The core emits structured operation errors for failed queued controls, while
|
|
133
|
+
synchronous legacy argument validation throws PlayerError. Rejected opens due
|
|
134
|
+
to invalid arguments or a signal already canceled may reject without an event;
|
|
135
|
+
callers must catch returned promises. Observer exceptions are reported through
|
|
136
|
+
the browser's error reporting and cannot veto a committed session. Close reserves
|
|
137
|
+
one teardown slot beyond the 32 ordinary pending-operation limit, so a full queue
|
|
138
|
+
cannot prevent cleanup. Browser resume requests initiated during another queued
|
|
139
|
+
operation may require a fresh user gesture when that operation completes.
|
|
140
|
+
|
|
141
|
+
Native exposes display dimensions; source rotation/codec metadata can remain null
|
|
142
|
+
when the browser does not report them. Its selected video entry is an opaque
|
|
143
|
+
source-scoped identity and does not imply a browser video-track switching API.
|
|
144
|
+
Live mpv seek windows use the demuxer's reported cached seekable timestamps; a
|
|
145
|
+
byte cache alone never creates a buffered-time claim. High-level time/state
|
|
146
|
+
notifications reuse backend events; only existing seek/presentation settlement
|
|
147
|
+
and engine health mechanisms retain their preexisting polling.
|
|
148
|
+
|
|
149
|
+
## Experimental playback optimizations
|
|
150
|
+
|
|
151
|
+
`experimentalBufferedNativeSeeks` and `experimentalHybridAudioFilters` are opt-in
|
|
152
|
+
constructor options, both false by default. Hybrid currently admits only scalar
|
|
153
|
+
`volume=N` / `lavfi=[volume=N]` attenuation for N in [0, 1]. Other filters retain
|
|
154
|
+
Software routing or explicit-mode rejection.
|
|
155
|
+
|
|
156
|
+
`audioGain` (default 1) and `setAudioGain(value)` request experimental scalar
|
|
157
|
+
attenuation in [0, 1]. All three modes use a dedicated, lazy Web Audio gain stage.
|
|
158
|
+
The setter updates that stage in place, preserving the engine, source, position,
|
|
159
|
+
and pause intent. Hybrid/Software gain follows the mpv PCM output without changing
|
|
160
|
+
the user audio filter chain. Ordinary volume and mute remain separate. Unity on a
|
|
161
|
+
fresh player allocates no gain stage; restoration retains an existing stage.
|
|
162
|
+
Transactional replacement is reserved for backends without an in-place gain method.
|
|
163
|
+
Diagnostics expose `plan` and `audioGain`; feature capabilities include `audioGain`.
|
|
164
|
+
See [current qualification and limitations](OPTIMIZATION-COMPLETION.md).
|
|
165
|
+
|
|
166
|
+
### Experimental selected-audio FLAC preparation
|
|
167
|
+
|
|
168
|
+
`experimentalAudioAdaptation: 'flac'` enables explicit Native trials after ordinary
|
|
169
|
+
Native/direct or packet-copy audio is unsuitable. It does not enable automatic
|
|
170
|
+
adaptation. The optional separately built preparation assets must be packaged.
|
|
171
|
+
The tested subset is integer PCM16/24 mono/stereo at 44.1/48 kHz; unsupported
|
|
172
|
+
precision/layouts reject instead of quantizing, downmixing or resampling. Video
|
|
173
|
+
packets are copied. No Opus permission is implied. It supports the separately
|
|
174
|
+
requested `audioGain` operation and external Native ASS, including their combination.
|
|
175
|
+
Long unequal tails have a separate qualified experimental FLAC path for desktop
|
|
176
|
+
Chrome, H264 and PCM16/24. It preserves the final video image while longer audio
|
|
177
|
+
continues, and silence after shorter audio ends. Distant seeks regenerate bounded
|
|
178
|
+
real preroll. Firefox rejects this Native timeline with `UNSUPPORTED_TIMELINE`;
|
|
179
|
+
use an eligible mpv route. On Firefox, a distant seek after video EOF can
|
|
180
|
+
recover from Hybrid through Software, with the failed seek disclosed in diagnostics.
|
|
181
|
+
Use Software explicitly when continuous ASS behavior through a Firefox video-EOF
|
|
182
|
+
audio tail is required; that Hybrid combination is not qualified.
|
|
183
|
+
Opus long tails remain unsupported. See the
|
|
184
|
+
[current qualification and reproductions](../results/optimization-final/README.md).
|
|
185
|
+
|
|
186
|
+
### Automatic lossless adaptation policy
|
|
187
|
+
|
|
188
|
+
```js
|
|
189
|
+
const player = new Player(container, {automaticAudioAdaptation: 'lossless'});
|
|
190
|
+
await player.open(file);
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
This optional policy permits Native FLAC only for inspected local Matroska with
|
|
194
|
+
H264 up to 1080p and selected 48 kHz PCM16/24 mono/stereo audio, with established
|
|
195
|
+
selected-track starts and ends within 50 ms. Original direct/copy playback remains
|
|
196
|
+
preferred. Unsupported sources retain eligible Hybrid/Software alternatives;
|
|
197
|
+
explicit modes remain authoritative. Selecting a different audio track repeats
|
|
198
|
+
admission while preserving its source identity and user intent. External Native
|
|
199
|
+
ASS and gain use their existing options and must form a supported complete plan.
|
|
200
|
+
|
|
201
|
+
This does not enable Opus, resampling, downmixing, embedded Native subtitles,
|
|
202
|
+
remote automatic adaptation or streaming adaptation. FLAC preserves admitted
|
|
203
|
+
integer decoded samples; it does not restore an earlier lossy recording.
|
|
204
|
+
Diagnostics disclose rejected alternatives and the accepted transformation.
|
|
205
|
+
|
|
206
|
+
### Experimental Native ASS/SSA
|
|
207
|
+
|
|
208
|
+
Set `experimentalNativeASS: true` to use external ASS/SSA with Native direct,
|
|
209
|
+
remux or qualified FLAC playback. Use the existing `addSubtitle(file)`,
|
|
210
|
+
`addFont(file)`, `selectSubtitleTrack(id)` and `subtitleVisible(value)` methods.
|
|
211
|
+
Fonts added before opening avoid a later transactional font refresh. Attachments
|
|
212
|
+
are source-scoped and reset on opening a new source. Source-scoped external IDs
|
|
213
|
+
follow attachment order across Native and mpv; they are distinct from embedded
|
|
214
|
+
stream indices. Other subtitle formats and embedded ASS still require mpv.
|
|
215
|
+
|
|
216
|
+
Install the optional libass assets using the package asset-copy command from a
|
|
217
|
+
package built with `--ass-build`. This feature requires cross-origin isolation.
|
|
218
|
+
Use container fullscreen; video-only fullscreen, PiP and casting cannot carry the
|
|
219
|
+
external overlay. Native PiP/remote-playback controls are disabled for this plan.
|
|
220
|
+
See [current coverage and limits](OPTIMIZATION-COMPLETION.md).
|
|
221
|
+
|
|
222
|
+
### Experimental selected-audio Opus preparation
|
|
223
|
+
|
|
224
|
+
`experimentalAudioAdaptation: 'opus'` requires `allowLossyAudio: true` and explicit
|
|
225
|
+
Native mode. It is a separate profile, never a fallback from failed FLAC. The
|
|
226
|
+
qualified input subset is copied H.264 video with selected PCM16/24 mono/stereo at
|
|
227
|
+
48 kHz. Other sample rates/layouts/codecs reject; permission to encode lossily does
|
|
228
|
+
not authorize resampling or downmixing. Original compressed audio remains preferred
|
|
229
|
+
when it can be copied. Opus+gain is supported; Opus+Native ASS is not admitted.
|
|
230
|
+
The matching optional preparation engine includes the pinned FFmpeg Opus encoder.
|
|
231
|
+
No sample-exact claim applies to Opus. See the current coverage document for delay,
|
|
232
|
+
padding, timestamp and marker tests and the unresolved long-tail limit.
|
|
233
|
+
|
|
234
|
+
### Complete-plan admission diagnostics
|
|
235
|
+
|
|
236
|
+
`diagnostics.plan` identifies the accepted internal plan without adding a public
|
|
237
|
+
mode. `diagnostics.planAdmission` lists finite candidates with `eligible`, typed
|
|
238
|
+
rejection `code`, and `reason`. Eligibility is permission to attempt the plan;
|
|
239
|
+
actual codec preparation and meaningful startup output must still succeed.
|
|
240
|
+
Automatic routing retains Native copy, authorized qualified Native FLAC, eligible Hybrid, then Software. It
|
|
241
|
+
uses requested effects, source inspection, selected tracks, transport constraints
|
|
242
|
+
and browser prerequisites. The experimental adaptation option remains explicit-only;
|
|
243
|
+
automatic FLAC uses the separate lossless policy above. Route ranking
|
|
244
|
+
does not use prototype benchmark percentages. Explicit mode selection remains
|
|
245
|
+
authoritative. In-place gain updates refresh plan diagnostics without rerouting.
|